Skip to content

feat: PreToolUse hook to constrain the executor to commands in experiment_plan.yaml #128

@sriumcp

Description

@sriumcp

TL;DR

Ship a Claude Code PreToolUse hook (nous-plan-enforcer) that intercepts Bash calls during EXECUTE_ANALYZE and rejects (or warns) if the command isn't derivable from experiment_plan.yaml.

Why this matters

The 5/18 mech-design-enforcement session showed two executor processes racing on the same iter dir — partly because nothing inside the agent enforced the plan. Hooks intercept tool calls deterministically before the LLM acts, so we can hard-guard the boundary.

What's already shipped

Proposed approach

  1. Define a JSON-Schema for experiment_plan.yaml commands (allowlist of binaries + arg patterns).
  2. Implement a Python script bin/nous-plan-enforcer that reads stdin (the proposed tool call), checks against the plan in CWD, exits 0 (allow) or non-zero (reject with reason).
  3. Register it as a PreToolUse hook in the per-campaign settings template (depends on Test issue #15).
  4. Two modes:
    • --strict → hook rejects unknown commands.
    • --warn (default) → hook lets them through but logs to iter-N/plan_violations.jsonl.
  5. Document the escape hatch (a special # nous: ad-hoc comment in a command).

Acceptance criteria

  • In strict mode, an executor that tries rm -rf / (or any unplanned command) is blocked.
  • In warn mode, violations land in plan_violations.jsonl with timestamp + arm context.
  • No false positives on the seven existing example campaigns.

Notes

  • This is a defense-in-depth measure on top of --dangerously-skip-permissions. It does not replace Test issue #15's permission policies.

Part of #120.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions