mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
[F099] wire pr_pass/pr_fail self_review block in the spec gate
The pr_pass/pr_fail ActionSpecs carry self_review_block=True, but _gate_preflight never populated Context.original_developer_slug, and actor_slug was read off agent.slug — which GatewayAgentView does not carry, so it was always None in production. The block was structurally dormant: a reviewer who was also the original developer of the assembled PR could pass (or fail) their own work. The service-layer _validate_not_self_review backstop only covers qa/documenter, not pr_reviewer, so the spec gate is the only defense. Set actor_slug=str(reviewer_agent_id) (GatewayAgentView has no slug, so the UUID is the identity) and original_developer_slug from the original_developer marker (a UUID stored as a string). Both resolve to UUID strings, so the spec's string-equality comparison fires when the reviewer IS the recorded original developer. The marker is never set on assembled coordination tasks (only on dev-leaf tasks at QA/doc claim), so the block stays dormant by design in production — but the gate is now correctly wired to fire if the marker were ever set to the reviewer. Zero production behavior change; the dormant-in-production state is pinned by the no-marker test.
This commit is contained in:
@@ -22,6 +22,7 @@ import structlog
|
||||
|
||||
from roboco.foundation.policy import lifecycle as spec_module
|
||||
from roboco.foundation.policy import tracing as _tr
|
||||
from roboco.foundation.policy.content import markers
|
||||
from roboco.services.gateway.envelope import Envelope
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -187,9 +188,21 @@ class PRGateMixin(_Base):
|
||||
)
|
||||
if isinstance(role, Envelope):
|
||||
return role
|
||||
# The spec gate's ``self_review_block`` is the only self-review defense
|
||||
# for pr_pass / pr_fail: the service-layer ``_validate_not_self_review``
|
||||
# backstop covers qa/documenter but skips pr_reviewer. For the comparison
|
||||
# to fire, both sides must be populated. ``GatewayAgentView`` carries no
|
||||
# ``slug`` field (so ``getattr(agent, "slug", None)`` is always None in
|
||||
# production), and the ``original_developer`` marker stores the dev's
|
||||
# UUID — so resolve both as UUID strings and let the spec's string
|
||||
# equality do the rest. The marker is never set on assembled coordination
|
||||
# tasks (only on dev-leaf tasks at QA/doc claim), so the block is dormant
|
||||
# by design in production — but the gate is now correctly wired to fire
|
||||
# if the marker were ever set to the reviewer.
|
||||
spec_ctx = spec_module.Context(
|
||||
actor_id=reviewer_agent_id,
|
||||
actor_slug=getattr(agent, "slug", None) if agent is not None else None,
|
||||
actor_slug=str(reviewer_agent_id),
|
||||
original_developer_slug=markers.get_original_developer(t),
|
||||
notes=notes,
|
||||
issues=issues,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user