Successor to #28, which is closed: b981aec blocked the six pure-judgment sign-offs at the MCP dispatcher, but three things it did not cover are still open. Splitting them out because #28's title ("baseline gate") no longer describes what's left.
1. Four tools still take any active user_id on trust
_require_human_user (src/opal/mcp/server.py:3982) is unchanged — it only checks that the supplied id matches an active User row. These four are not in _HUMAN_ONLY_SIGNOFFS, so they still reach it and an agent can pass any discoverable id:
join_execution
focus_step
complete_step
bind_issue_hold
complete_step is the sharp one: it attributes procedure work to a named person in the execution record. Worth deciding per tool against the principle b981aec established — objective/blocker-gated acts may be delegated by admin opt-in, pure-judgment sign-offs may not. Completing a step is plausibly the former (an operator working through a procedure with agent assistance), but "any id passes" is not what opt-in means either.
2. The old gate is now dead code in the blocked handlers
All six blocked tools are refused before dispatch, so the _require_human_user calls inside _sign_disposition, _accept_risk, _stamp_risk_review, _baseline_batch and _reaffirm_requirement never execute (each handler has exactly two references: its definition and the unreachable dispatch line).
_baseline_requirement is worse than dead — it carries an inlined copy of the gate (server.py:3948-3960) rather than calling the helper. If anyone hardens _require_human_user to fix item 1, that path silently would not get it. One fact, one home: delete the inlined copy and the unreachable calls, or make the helper the single gate and drive the block from it.
3. publish_version has no human gate
Dispatched at server.py:2233 with no check at all. It publishes immutable procedure versions — an authoritative act by the same reasoning that made baselining human-only. b981aec flagged this itself as out of scope and noted it on risk #76:
publish_version publishes immutable procedure versions with no human gate today — related but out of this scope; noted on risk #76 for follow-up.
🤖 Generated with Claude Code
Successor to #28, which is closed: b981aec blocked the six pure-judgment sign-offs at the MCP dispatcher, but three things it did not cover are still open. Splitting them out because #28's title ("baseline gate") no longer describes what's left.
1. Four tools still take any active
user_idon trust_require_human_user(src/opal/mcp/server.py:3982) is unchanged — it only checks that the supplied id matches an activeUserrow. These four are not in_HUMAN_ONLY_SIGNOFFS, so they still reach it and an agent can pass any discoverable id:join_executionfocus_stepcomplete_stepbind_issue_holdcomplete_stepis the sharp one: it attributes procedure work to a named person in the execution record. Worth deciding per tool against the principle b981aec established — objective/blocker-gated acts may be delegated by admin opt-in, pure-judgment sign-offs may not. Completing a step is plausibly the former (an operator working through a procedure with agent assistance), but "any id passes" is not what opt-in means either.2. The old gate is now dead code in the blocked handlers
All six blocked tools are refused before dispatch, so the
_require_human_usercalls inside_sign_disposition,_accept_risk,_stamp_risk_review,_baseline_batchand_reaffirm_requirementnever execute (each handler has exactly two references: its definition and the unreachable dispatch line)._baseline_requirementis worse than dead — it carries an inlined copy of the gate (server.py:3948-3960) rather than calling the helper. If anyone hardens_require_human_userto fix item 1, that path silently would not get it. One fact, one home: delete the inlined copy and the unreachable calls, or make the helper the single gate and drive the block from it.3.
publish_versionhas no human gateDispatched at
server.py:2233with no check at all. It publishes immutable procedure versions — an authoritative act by the same reasoning that made baselining human-only. b981aec flagged this itself as out of scope and noted it on risk #76:🤖 Generated with Claude Code