Skip to content

governance: productionize policy approval checkpoint coverage #1980

Open

Description

@mashkovd

Parent: mctlhq/.github#181
Source: mctlhq/.github#182 decision-assurance audit (G5 / P4).

Problem

The decision-assurance audit found policy-approval checkpoint code/contracts but no evidence that MCTL_POLICY_APPROVALS=mctl-api is enabled in current GitOps deployment declarations. Some transports/entry points (including Bash/mentor paths identified by the audit) are also outside the canonical checkpoint.

A governance capability cannot be considered complete while the canonical policy checkpoint is optional/disabled or bypassable by normal supported entry points.

Goal

Make the canonical policy approval checkpoint an explicit production contract for governed actions, or explicitly classify paths as non-governed with enforceable boundaries.

Scope

  • Verify current deployment configuration and supported entry points.
  • Enable the canonical mctl-api-backed checkpoint where the action is classified governed.
  • Close normal transport gaps or explicitly exclude them with tests/documented contract.
  • Fail closed when a governed action requires policy/approval evidence and the checkpoint is unavailable.
  • Reuse existing policy/approval contracts; no second policy engine.

Acceptance criteria

  • Production GitOps declarations explicitly configure the canonical checkpoint for governed paths.
  • Supported governed transports cannot bypass the checkpoint by selecting another normal entry point.
  • Unavailable/misconfigured checkpoint fails closed for governed actions with typed evidence.
  • Deployment/test evidence demonstrates the configured path end-to-end.
  • Any intentionally non-governed transport is explicitly classified and cannot be mistaken for governed execution.

Refs mctlhq/.github#181, mctlhq/.github#182.

Activity

  1. mctl-agents commented on Oct 19, 2026

    @mctl-agents
    Contributor

    mctl-agents issue-investigator has analyzed this issue and created a proposal:

    https://github.com/mctlhq/mctl-gitops/tree/main/platform-gitops/agents-state/mctl-gitops/proposals/issue-1980-governance-productionize-policy-approval/

    Status: proposed — pending human approval. Review requirements.md, design.md and tasks.md, then approve: signal this issue's DevLoopWorkflow (mctl-api POST /api/v1/agents/dev-loop/dev-loop-mctlhq-mctl-gitops-1980/approve, or python -m orchestrator.temporal.cli approve dev-loop-mctlhq-mctl-gitops-1980 --approver <your-identity>) — the workflow flips .status.yaml to accepted via the mctl-agents-approve operation and runs the Tier 2 implementer. If no DevLoopWorkflow is running for this issue (pre-Temporal proposal), run the mctl-agents-approve operation directly with service=mctl-gitops slug=issue-1980-governance-productionize-policy-approval.

    The approver is recorded in .status.yaml and in the gitops commit, and the implementer refuses a proposal whose control.requires_human_approval carries no named approver — so prefer the mctl-api endpoint, which takes the approver from the authenticated caller rather than from what the caller types.

  2. added 2 commits that reference this issue on Oct 19, 2026
  3. mctl-agents commented on Oct 19, 2026

    @mctl-agents
    Contributor

    Approval grant aar_9313b2e634e74a579fc8f087b157dc24 was superseded because the proposal was rewritten. It was bound to digest sha256:aa6556ba2da974cc88b1c81071da73287ee155a34737ad63c74bc07653c3b987; the current digest is sha256:e275b68c5ccbea413d82e88d39f398f660c88ce0d0fd4b5c11e8923e79ee3647. Please decide the fresh grant aar_fff21ba34dab4a589b94c6ca7dad41ad.

  4. mashkovd commented on Oct 23, 2026

    @mashkovd
    MemberAuthor

    Receipt gate enforce is live (#2101, merge c25f0c2, 2026-10-23 ~17:35Z). Both running mctl-api replicas print MCTL_AGENT_RECEIPT_GATE=enforce; rollout completed cleanly.

    Evidence at the flip:

    • Observe (2026-10-20 20:57Z to the flip) recorded 0 agent-receipt-gate rows: service:mctl-agent made no governed MCP calls, only reads, so enforce refuses no legitimate path seen.
    • Investigate, the only surface with MCTL_POLICY_APPROVALS=mctl-api, now calls MCP on scoped run tokens (mctl-agents#737, feat(agents): enforce run tokens for investigate again #2099). Two enforced runs on 2026-10-23 (bc6d5846, a765878a) succeeded, each with a token exchange and an agent_scope.denied row for the out-of-scope probe.
    • Agent principals are not gated by the receipt gate; no run-token scope grants a governed tool, and test(mcp): no agent run scope may admit a receipt-gated tool mctl-api#775 pins that with a CI test.

    Still open against this issue's acceptance criteria: the Bash / MCTL_TOKEN REST path stays bypass-open in the coverage manifest (mctl-agents#717), and implement/run/shepherd/reconcile remain rollout: staged. Follow-ups: mctlhq/mctl-api#772 (spend after success), mctlhq/mctl-api#773 (receipt age bound).

  5. mashkovd commented on Oct 23, 2026

    @mashkovd
    MemberAuthor

    Status after the staged-surface flips (owner OK 2026-10-24, one OK per surface).

    Done

    Acceptance criteria

    • Production declarations configure the checkpoint for every surface classified governed.
    • No bypass through another normal entry point. Not yet. investigate-bash, implement-bash, shepherd-bash and run-bash stay bypass-open (policy: keep the mctl-api token out of agent tool subprocesses mctl-agents#717): the static MCTL_TOKEN is still in those containers. The owner chose option (c): move the runs' REST calls onto their scoped run token, then drop MCTL_TOKEN from the CWFT. The first slice is merged and live: mctl-api#779 (4.119.0) lets an investigate run token seal its own execution's snapshot. Still to do:
      • mctl-agents: switch WorkItemClient to the run token.
      • The self-attaching runs, whose token is not bound to a work item.
      • Action approvals for run tokens.
      • Dropping MCTL_TOKEN from the investigate CWFT. The other surfaces follow the same path.
    • Fails closed when unavailable or misconfigured:
      • An unknown MCTL_POLICY_APPROVALS value turns every REQUIRE_APPROVAL into a lookup error.
      • A store that cannot be reached returns DENY.
      • The receipt gate is in enforce mode.
    • End-to-end evidence on the newly flipped surfaces. Partial. POLICY_DECISION shows which backend answered only on a REQUIRE_APPROVAL decision (approval_pending with an aar_ ref, compared with approval_required under NO_APPROVALS). No run since the flip has made a governed call. The runs observed so far were all ALLOW by rule, and none failed.
    • Non-governed paths are classified explicitly, and the coverage test makes that machine-checked.

    Keeping this open until #717 removes the bypass-open entries.

  6. mashkovd commented on Oct 24, 2026

    @mashkovd
    MemberAuthor

    investigate-bash is no longer bypass-open (#2118, merged as 79e191f, mctl-agents 1.98.0 from mctlhq/mctl-agents#788).

    Evidence:

    • Before the template change, organic DevLoop run mctl-agents-investigate-2c02f23a (mctl_list_previews reports count 0 when the ArgoCD query fails mctl-api#698, image 1.98.0, we_ execution). The pod log shows run token: enforce, scope=investigate and the out-of-scope probe refused. The work_context read succeeded and WORK_CONTEXT_SNAPSHOT ... snapshot-sealed followed. mctl-api audit records work_item.snapshot succeeded with user agent:issue-investigator.

    • After the template change, organic DevLoop run mctl-agents-investigate-d65f1786 (fix(implementer): use Refs, not Closes, in PRs for spike issues mctl-agents#763, submitted 10:22Z after Argo CD synced). The stored run-investigator template maps no MCTL_TOKEN. mctl-api audit shows, in order:

      • agent_scope.denied (10:22:25), the enforced out-of-scope probe;
      • work_item.snapshot succeeded by agent:issue-investigator (10:22:28);
      • agent_run_token.revoke succeeded (10:24:43).

      The proposal was committed and the run Succeeded.

    Still bypass-open: implement-bash, run-bash, shepherd-bash. Each needs a scope profile and an exchange-template entry, and its REST clients (ClaimClient, approvals, dev-loop GET) must move onto the run token. The approvals and self-attach design questions are open on mctlhq/mctl-agents#717. This issue stays open.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions