fix(qwen-ops): route memory commands through runner bridge - #32
Merged
Merged
Conversation
…s' into codex/qwen-ops-quick-command-args
🔎 Lint report:
|
| Rule | Count |
|---|---|
deprecated |
1 |
unresolved-attribute |
1 |
not-subscriptable |
1 |
First entries
gateway/run.py:109: [deprecated] deprecated: The function `utcnow` is deprecated: Use timezone-aware objects to represent datetimes in UTC; e.g. by calling .now(datetime.timezone.utc)
tests/gateway/test_qwen_ops_routed_client.py:376: [unresolved-attribute] unresolved-attribute: Attribute `name` is not defined on `None` in union `CommandDef | None`
tests/hermes_cli/test_mac_memory_commands.py:125: [not-subscriptable] not-subscriptable: Cannot subscript object of type `object` with no `__getitem__` method
✅ Fixed issues: none
Unchanged: 4738 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
added 4 commits
June 7, 2026 16:16
# Conflicts: # gateway/run.py # hermes_cli/commands.py # hermes_cli/mac_memory_commands.py # tests/hermes_cli/test_mac_memory_commands.py
Collaborator
Author
audit_validation:
verdict: PASS_WITH_NOTES
material_findings:
- id: MF-01
severity: NOTE
location: gateway/run.py — _record_qwen_ops_memory_ingress
finding: >
Denied/unauthorized memory command invocations produce no ingress receipt.
The receipt is only written after _qwen_ops_memory_source_allowed returns True.
Unauthorized probes (wrong platform, unlisted chat_id) leave no audit trail.
Not a spec violation, but creates a blind spot for access-attempt forensics.
- id: MF-02
severity: NOTE
location: hermes_cli/commands.py — slack_native_slashes()
finding: >
The telegram_bot_menu=False flag suppresses Telegram setMyCommands entries
but there is no corresponding slack_bot_menu flag. Memory commands are excluded
from Slack native slashes only by seeding-order + 50-command-cap pressure
(Telegram visible list seeded first, memory commands fall off the back).
If the total command count is < 50, memory commands can appear in Slack native
menus. No test exercises slack_native_slashes() output to verify exclusion.
This is a pre-existing gap — not a regression introduced by this PR — but the
PR's stated intent ("hidden from native Slack/Telegram menus") is only
probabilistically enforced on the Slack side.
- id: MF-03
severity: NOTE
location: hermes_cli/mac_memory_commands.py — _safe_remote_helper_host / _remote_helper_argv
finding: >
REMOTE_HELPER_HOST option-injection rejection is correct and tested.
REMOTE_HELPER_DIR is not validated (could be set to a path starting with "-"),
but shlex.quote wraps the full remote_helper path before SSH sees it as a remote
command, not an SSH option. Risk is low; still worth a validation guard or at
least a REMOTE_HELPER_DIR.startswith("-") check symmetric with the host check.
- id: MF-04
severity: NOTE
location: hermes_cli/mac_memory_commands.py:13-14
finding: >
REMOTE_HELPER_HOST and REMOTE_HELPER_DIR are evaluated at module import time.
Tests compensate with monkeypatch.setattr on the module attribute, which is
correct. Runtime env changes after import are not picked up — acceptable for
a daemon that reads config at startup, but worth documenting as a known
constraint.
unresolved_blockers: []
protected_claims_checked:
- claim: Telegram delivery proof (response_sent)
status: CORRECTLY_REJECTED — field absent from receipt; test asserts
`"response_sent" not in payload`. response_delivery_verified=False. ✓
- claim: TELEGRAM_HOME_CHANNEL authorizes memory bridge
status: CORRECTLY_REJECTED — _qwen_ops_memory_source_allowed reads only
QWEN_OPS_MEMORY_ALLOWED_CHATS; test
test_qwen_ops_memory_command_ignores_general_telegram_home_channel
verifies TELEGRAM_HOME_CHANNEL=<chat_id> is insufficient. ✓
- claim: qwen-ops merge/approve/comment/label authority
status: NOT_INTRODUCED — diff adds no approve/merge/label/comment path;
existing test test_qwen_ops_has_no_extra_write_authority remains
unchanged and passing. ✓
- claim: branch-protection bypass
status: NOT_INTRODUCED — no runner/workflow/CI config touched. ✓
- claim: bridge_invoked=true on non-PASS response
status: CORRECTLY_CONSTRAINED — startswith("PASS ") (with trailing space);
test_qwen_ops_memory_ingress_marks_pre_helper_failures_not_invoked
verifies FAIL response sets bridge_invoked=False. ✓
receipt_summary: >
Receipts correctly distinguish response_generated (True) from delivery proof
(response_delivery_verified=False, no response_sent field), with bridge_invoked
gated on a "PASS " prefix; all three claims are tested and verified.
ten_x_two_x_recommendations:
- classification: DO_NOW
rationale: >
Add a denied-attempt receipt path in _handle_message (or a parallel
_record_qwen_ops_memory_ingress_denied helper) so unauthorized probes are
logged with result="denied", bridge_invoked=False before the early return.
Without this, security-relevant access attempts are invisible in the audit log.
acceptance_boundary: >
A JSONL entry per denied attempt with event_type, command, chat_source, chat_id,
bridge_invoked=False, result="denied", response_generated=False is sufficient.
Must not contain the denied message text (no payload exposure).
- classification: FOLLOW_UP_PACKET
rationale: >
Add a test for slack_native_slashes() asserting all six memory-* commands are
absent, or introduce a slack_bot_menu=False flag symmetric with telegram_bot_menu
so the exclusion is enforced by policy rather than cap pressure. The current
ordering heuristic is fragile as the command registry grows.
acceptance_boundary: >
Either an explicit filter on a new flag in slack_native_slashes(), or a test
that calls slack_native_slashes() and asserts no memory-* name appears, covers
the gap. The flag approach is preferable for long-term robustness.
- classification: FOLLOW_UP_PACKET
rationale: >
Add a _safe_remote_helper_dir guard (reject values starting with "-") symmetric
with _safe_remote_helper_host, and test it. shlex.quote prevents shell injection
in the remote command, but defense-in-depth at the argv-construction layer is
cheap and makes the security contract explicit.
acceptance_boundary: >
One predicate function + one test covering "-oProxyCommand=..." dir value
returning None from _remote_helper_argv.
- classification: NO_HIGH_LEVERAGE_OPPORTUNITY_FOUND
rationale: >
Core auth boundary (Telegram + QWEN_OPS_MEMORY_ALLOWED_CHATS), receipt
schema, SSH injection rejection, and menu hiding are all correctly implemented
and tested. No structural refactor or performance optimization is warranted
given the narrow advisory-only scope of qwen-ops.
acceptance_boundary: N/A |
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?
STATUS_ONLY
not_merge_evidence: true
This PR repairs qwen-ops Hermes
/memory-*ingress so explicitly allowlisted qwen-ops Telegram chats can call the qwen-ops-01 Mac memory bridge helpers through the shared Hermes gateway. It keeps qwen-ops as a Hermes-routed, advisory/local-ops client, hides memory helpers from native bot menus, and records receipts that do not claim platform delivery verification.Related Issue
Fixes #
Type of Change
Changes Made
hermes_cli/mac_memory_commands.py: add SSH fallback toqwen-ops-01bridge helpers when local helper binaries are unavailable on the Mac, with option-injection host rejection and structuredFAIL ... error=EXIT_<code>transport failures.gateway/run.py: require explicitQWEN_OPS_MEMORY_ALLOWED_CHATSTelegram chat allowlisting for qwen-ops memory bridge calls and record receipts withresponse_generated,response_delivery_verified: false, and noresponse_generated/response_delivery_verifiedoverclaim.tests/hermes_cli/test_mac_memory_commands.py: cover SSH fallback, stdin forwarding for/memory-write, structured SSH failure, and host option-injection rejection.tests/gateway/test_qwen_ops_routed_client.py: cover Telegram allowlist gating,TELEGRAM_HOME_CHANNELnon-authorization, hidden menu/routable behavior,bridge_invokedPASS-only receipts, and noresponse_generated/response_delivery_verifiedoverclaim.How to Test
.venv/bin/python -m pytest tests/hermes_cli/test_commands.py tests/hermes_cli/test_mac_memory_commands.py tests/gateway/test_qwen_ops_routed_client.py -q.venv/bin/python -m py_compile hermes_cli/mac_memory_commands.py gateway/run.py hermes_cli/commands.pygit diff --checkRisk, Complexity, Review, and CI Classification
/memory-*commands can invoke the qwen-ops-01 memory bridge only from explicitly allowlisted Telegram chats and record actual Telegram ingress receipts; receipts distinguish response generation from unverified platform delivery, and score remains capped until real chat-origin commands are observed/memory-*receipts showchat_source=actual Telegram,bridge_invoked=true, andresponse_generated=true and response_delivery_verified=falsePolicy decision output
Non-claims
Review Receipt
Review Receipt
Review Receipt
Review Receipt
RuntimePayloadContract
/memory-*commands have a runtime path to the qwen-ops-01 Mac memory bridge and can emit ingress receipts when real Telegram messages are handled.gateway/run.pyslash-command dispatch andhermes_cli/mac_memory_commands.pyhelper execution.tests/hermes_cli/test_mac_memory_commands.py,tests/gateway/test_qwen_ops_routed_client.py, py_compile, diff check, and direct Hermes handler smoke to qwen-ops SSH bridge./memory-*commands must produce receipt events withchat_source=actual Telegram,bridge_invoked=true, andresponse_generated=true and response_delivery_verified=falsebefore 9.5+ can be claimed.ORG_MEMORY_REMOTE_HELPER_HOST/ORG_MEMORY_REMOTE_HELPER_DIRempty to disable SSH fallback; existing non-memory Hermes chat behavior remains unchanged.Checklist
Code
Documentation & Housekeeping
cli-config.yaml.exampleif I added/changed config keys — N/A.CONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — N/A.For New Skills
Screenshots / Logs
165 passed;git diff --checkclean;py_compile hermes_cli/mac_memory_commands.py gateway/run.py hermes_cli/commands.pyclean.PASS.response_generated: true,response_delivery_verified: false, and omitsresponse_generated/response_delivery_verified. Slack/Telegram native menus keep qwen-ops memory commands hidden while internal routing remains allowlisted.