fix(google-workspace): restore required_credential_files in SKILL.md (#16452) - #16470
Conversation
…ousResearch#16452) PR NousResearch#9931 ("feat(google-workspace): add --from flag for custom sender display name") accidentally removed the required_credential_files frontmatter block that tells hermes to bind-mount google_token.json and google_client_secret.json into Docker and Modal remote terminals before running setup.py. Without this header the credential files are never registered in the session-scoped ContextVar, so get_credential_file_mounts() returns an empty list at container creation time and the OAuth files are invisible inside the sandbox. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Restores the required_credential_files YAML frontmatter in the Google Workspace skill so Hermes can register/mount OAuth credential files into remote terminal backends (Docker/Modal), and adds a regression test to prevent the header from being dropped again.
Changes:
- Re-adds
required_credential_filesentries forgoogle_token.jsonandgoogle_client_secret.jsontoskills/productivity/google-workspace/SKILL.md. - Adds a new test suite validating the frontmatter presence and basic credential-file registration/mount behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
skills/productivity/google-workspace/SKILL.md |
Restores the required_credential_files frontmatter block needed for credential passthrough into remote sandboxes. |
tests/skills/test_google_workspace_credential_files.py |
Adds regression coverage to ensure the frontmatter field exists and registers/mounts expected credential files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| with patch.dict(os.environ, {"HERMES_HOME": str(hermes_home)}): | ||
| missing = register_credential_files(entries) | ||
|
|
||
| assert missing == [], f"Unexpected missing files: {missing}" | ||
| mounts = get_credential_file_mounts() | ||
| container_paths = {m["container_path"] for m in mounts} | ||
| assert "/root/.hermes/google_token.json" in container_paths | ||
| assert "/root/.hermes/google_client_secret.json" in container_paths |
There was a problem hiding this comment.
This test calls get_credential_file_mounts() after only patching HERMES_HOME inside the register_credential_files() block. get_credential_file_mounts() loads terminal.credential_files via read_raw_config() on first use, so it can read the developer machine’s real ~/.hermes/config.yaml and add mounts (including google_token.json) that make the test environment-dependent/flaky. Patch HERMES_HOME for the entire section that calls get_credential_file_mounts(), and/or explicitly reset/disable tools.credential_files._config_files (similar to tests/tools/test_credential_files.py) so config-based mounts can’t leak into these assertions.
| with patch.dict(os.environ, {"HERMES_HOME": str(hermes_home)}): | ||
| missing = register_credential_files(entries) | ||
|
|
||
| assert "google_token.json" in missing | ||
| mounts = get_credential_file_mounts() | ||
| container_paths = {m["container_path"] for m in mounts} | ||
| assert "/root/.hermes/google_client_secret.json" in container_paths | ||
| assert "/root/.hermes/google_token.json" not in container_paths |
There was a problem hiding this comment.
Same config-leak risk here: HERMES_HOME is only patched around register_credential_files(), but get_credential_file_mounts() may still read the real ~/.hermes/config.yaml (terminal.credential_files) and include extra mounts. If a developer has google_token.json configured, the assertion that it is not mounted will fail even though registration behaved correctly. Keep HERMES_HOME patched while calling get_credential_file_mounts(), and/or clear tools.credential_files._config_files between tests to keep mounts deterministic.
| (e["path"] if isinstance(e, dict) else e) | ||
| for e in entries |
There was a problem hiding this comment.
The set comprehension uses e["path"] for dict entries, which will raise KeyError and produce a less-informative failure if the entry schema ever changes (note register_credential_files also supports dict entries with a "name" key). Consider using e.get("path")/e.get("name") (and skipping falsy values) so the test fails with the intended assertion message rather than crashing.
| (e["path"] if isinstance(e, dict) else e) | |
| for e in entries | |
| path | |
| for e in entries | |
| for path in [((e.get("path") or e.get("name")) if isinstance(e, dict) else e)] | |
| if path |
Three Copilot findings addressed:
1. Config-cache leak: both integration tests called get_credential_file_mounts()
outside the patch.dict(HERMES_HOME) context, so _load_config_files() could
still read the real ~/.hermes/config.yaml. Move the call inside the with
block and reset _config_files to None before each test to ensure a fresh
read under the controlled env.
2. Set-comprehension KeyError: e["path"] raises if a dict entry has no "path"
key. Use e.get("path") with a conditional filter instead.
3. Unused import: removed import pytest (no pytest.mark/raises/fixture usage).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@copilot-pull-request-reviewer All 3 findings addressed in commit Finding 1 (line 71 — config-cache leak in Finding 2 (line 100 — same leak in Finding 3 (line 40 — Also removed the unused |
|
Salvaged via #19886 onto current main. Thanks @briandevans! |
Summary
required_credential_filesfrontmatter block removed in feat(google-workspace): add --from flag for custom sender display name #9931 fromskills/productivity/google-workspace/SKILL.mdThe bug
PR #9931 ("feat(google-workspace): add --from flag for custom sender display name") accidentally dropped the
required_credential_filesYAML header while reformatting the frontmatter. This header is the mechanism by which hermes registersgoogle_token.jsonandgoogle_client_secret.jsonfor bind-mounting into Docker/Modal remote terminal backends at container creation time.Without this header,
register_credential_files()is never called for the skill, the session-scopedContextVaris never populated, andget_credential_file_mounts()returns an empty list whenDockerEnvironment.__init__builds the container's-varguments. The OAuth credential files are therefore never visible inside the sandbox —setup.pyfails to find them even though they exist on the host.The fix
Restores both
google_token.jsonandgoogle_client_secret.jsontorequired_credential_files. The existingregister_credential_file()implementation skips files that don't exist on the host (first-time setup,google_token.jsonabsent) without error and adds them to themissing_cred_fileslist that drivessetup_needed = True, so the setup prompt behaviour is correct.Test plan
required_credential_filesabsent →fm.get("required_credential_files")returnsNone→test_required_credential_files_present_in_skill_mdfails"required_credential_files missing from google-workspace SKILL.md"Related
🤖 Generated with Claude Code