Conversation
557fc1e to
8562c4b
Compare
8562c4b to
1016047
Compare
|
Thanks for the focused opt-in policy contract. Current main confirms the reported premise: I found no concrete correctness or design-fit defect in the reviewed diff. The implementation extends the existing plugin hook/resolver surface, adds no core model tool or environment variable, and keeps ordinary hooks fail-open. GitHub CI is green, including Python e2e and all test slices. Automated hermes-sweeper review. |
Ship blocker: unreadable or malformed config disables the required policyExact head reviewed: The new gate fails open when the configuration file cannot be parsed/read, or when
That means a profile with a working required guard can be changed to malformed YAML, an unreadable config, or Smallest safe repair:
The rest of the required-plugin ownership/callback/explicit-decision path looks well aligned with #61656, and current CI is green. This configuration-health gap is the remaining concrete fail-closed blocker I found. |
Follow-up ship blocker: plugin commands bypass the gate entirelyI completed a second exact-head dispatch-path audit at
A plugin command can therefore invoke a protected mutating tool with zero required-policy evaluations, including in gateway mode. That alone prevents this head from satisfying #61656 or Product Factory #47. Required repair and proof:
Combined verdict for this exact head: request changes. The required-policy core is promising, but configuration discovery and this public dispatch path still fail open. |
1016047 to
4bbe764
Compare
|
@rmacbot Both requested follow-ups are now implemented in commit
Validation includes Addresses: |
4bbe764 to
ae96831
Compare
|
Refreshed onto current |
ae96831 to
1766888
Compare
|
Rebased onto current Validation on the rebased head:
GitHub reports |
1766888 to
5f7413d
Compare
|
Rebased onto current The resolution preserves current main's first-party lifecycle observation for every pre-tool call, including required-policy calls, while retaining ownership-aware detailed plugin outcomes for explicitly protected tools. Ordinary hooks still use the centralized fail-open lifecycle path. A regression now proves required policies emit the first-party observation exactly once. Validation on the rebased head:
GitHub now reports the PR |
5f7413d to
0982e03
Compare
|
Rebased onto current main at 9460cc1 and composed the plugin conflict with main's central ownership ledger, callback payload filtering, managed-scope dispatch, and lifecycle observation. Required policies remain owner-aware and fail closed only for explicitly protected tools.\n\nNew head: 0982e03ba\n\nValidation:\n- broad plugin/hook/dispatch suite: 311 tests passed\n- final exact-base focused suite: 182 tests passed\n- Ruff, byte-compilation, diff check, publish gate, and gitleaks passed\n- replacement GitHub CI passed all required checks; GitHub reports MERGEABLE. |
0982e03 to
0cefe26
Compare
|
Rebased onto current The resolution composes required-policy ownership and fail-closed behavior with upstream's argument-modifying hooks and centralized dispatch helper. Required policies are validated first; ordinary modifiers still accumulate; both the public resolver and every centralized execution sink fail closed on a resolver crash only when the tool is explicitly protected. New regressions cover a required allow-policy coexisting with another plugin's argument modification and protected versus unprotected dispatch crashes. Validation on the final base: 200 focused plugin/config/model-tool tests, 156 adjacent execution-path tests, and all 258 full run-agent tests passed. Ruff, diff checks, public identity, overlap review, gitleaks, and the existing-PR publish gate passed. Replacement GitHub CI also passed completely, including all 12 Python shards, lint/security/attribution gates, and both Docker architectures. GitHub reports the PR mergeable. |
Closes #61656
Summary
plugins.entries.<plugin_id>.required_pre_tool_callcontract for protected tool-name globsapprovedecisions through the existing fail-closed human approval gatePluginContext.dispatch_tool()callsSecurity behavior
A matching required policy must be loaded and enabled, must own at least one
pre_tool_callcallback, and every owned callback must return an explicitallow, validblock, or validapprovedecision. Failure responses are sanitized; callback, loader, and approval exception details are logged but not returned to the model.An absent config remains valid. Once a config source exists, policy evaluation uses a strict read and does not substitute defaults or last-known-good data for malformed or unreadable current content.
All current dispatch paths converge on
resolve_pre_tool_block, including direct model dispatch, sequential/concurrent executors, runtime helpers, tool-search resolved calls, and plugin-command dispatch.Validation
uv run pytest -q tests/plugins(1801 passed)uv run ruff check .uv run python scripts/check-windows-footguns.py --all(761 files clean)uv run python -m py_compile hermes_cli/config.py hermes_cli/managed_scope.py hermes_cli/plugins.pygit diff --checkTests use temporary Hermes homes and mocked approval/dispatch boundaries; no live providers, gateways, credentials, or MCP servers were used.