{"id":50068456,"url":"https://github.com/conalh/policymesh","last_synced_at":"2026-05-29T04:00:52.413Z","repository":{"id":359417540,"uuid":"1245997554","full_name":"Conalh/PolicyMesh","owner":"Conalh","description":"Cross-surface AI agent policy consistency review.","archived":false,"fork":false,"pushed_at":"2026-05-22T02:18:42.000Z","size":147,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T04:23:50.082Z","etag":null,"topics":["ai-agents","claude","codeium","codex","cursor","github-action","mcp","policy-as-code","vscode"],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Conalh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-21T19:14:29.000Z","updated_at":"2026-05-22T01:58:42.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Conalh/PolicyMesh","commit_stats":null,"previous_names":["conalh/policymesh"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/Conalh/PolicyMesh","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Conalh%2FPolicyMesh","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Conalh%2FPolicyMesh/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Conalh%2FPolicyMesh/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Conalh%2FPolicyMesh/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Conalh","download_url":"https://codeload.github.com/Conalh/PolicyMesh/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Conalh%2FPolicyMesh/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33635961,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-29T02:00:06.066Z","response_time":107,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agents","claude","codeium","codex","cursor","github-action","mcp","policy-as-code","vscode"],"created_at":"2026-05-22T01:04:44.847Z","updated_at":"2026-05-29T04:00:52.404Z","avatar_url":"https://github.com/Conalh.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PolicyMesh\n\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)\n[![Language: TypeScript](https://img.shields.io/badge/language-TypeScript-3178c6.svg)](https://www.typescriptlang.org/)\n[![Local-only](https://img.shields.io/badge/local--only-uploads%20nothing-2ea44f.svg)](#how-it-works)\n[![Release](https://img.shields.io/github/v/release/Conalh/PolicyMesh)](https://github.com/Conalh/PolicyMesh/releases)\n\n**An agent-policy drift detector for reproducible AI-assisted development.** PolicyMesh finds contradictory instructions and configuration before stale repo rules make agents behave differently from run to run.\n\nAgent policy now lives across `CLAUDE.md`, `AGENTS.md`, Cursor rules, Copilot instructions, MCP configs, Codex config, VS Code settings, Windsurf, Aider, and repo docs. PolicyMesh reads that whole surface in the checked-out repository, compares the rules that different tools see, and turns contradiction drift into a reviewable report.\n\n```mermaid\nflowchart LR\n    Instructions[\"Instruction files\u003cbr/\u003eCLAUDE.md · AGENTS.md · Cursor rules\u003cbr/\u003eCopilot instructions\"] --\u003e Mesh\n    Configs[\"Agent configs\u003cbr/\u003eMCP · Codex · VS Code\u003cbr/\u003eWindsurf · Aider\"] --\u003e Mesh\n    PR[\"Pull request diff\u003cbr/\u003ebase vs head\"] --\u003e Mesh\n    Mesh[(\"PolicyMesh\u003cbr/\u003econtradiction + drift audit\")] --\u003e Report[\"Review output\u003cbr/\u003ePR annotations · Markdown\u003cbr/\u003eJSON · SARIF\"]\n    Report --\u003e Decision[\"Reviewable policy decisions\"]\n\n    classDef input fill:#1e293b,stroke:#334155,color:#e2e8f0\n    classDef engine fill:#0f172a,stroke:#1e293b,color:#e2e8f0,stroke-width:2px\n    classDef output fill:#0c4a6e,stroke:#0369a1,color:#e0f2fe\n    class Instructions,Configs,PR input\n    class Mesh engine\n    class Report,Decision output\n```\n\nShips as a local-only TypeScript CLI and GitHub Action. One audit pass can produce terminal text, Markdown step summaries, PR annotations, JSON for [GovVerdict](https://github.com/Conalh/GovVerdict), or SARIF for SAST consumers.\n\n**See also:** [agent-gov-core](https://github.com/Conalh/agent-gov-core) for the shared report schema · [agent-gov-demo](https://github.com/Conalh/agent-gov-demo) for an end-to-end sample PR · [ScopeTrail](https://github.com/Conalh/ScopeTrail) for PR-level permission drift.\n\n## Why this exists\n\nAI-agent behavior is now controlled by a pile of repo-local policy files. They are edited by different people, at different times, for different tools. A contradiction does not fail CI. It just changes what the agent believes it is allowed to do.\n\nMost drift is not malicious. It is a teammate editing one agent file weeks after another file already defined a different rule. Reviewers see one file at a time in a PR. The next agent session just receives conflicting policy and silently chooses which rule to follow.\n\nPolicyMesh exists to make those contradictions visible before they turn into nondeterministic agent behavior.\n\n## What it catches\n\n| Drift class | Example |\n| --- | --- |\n| **Instruction drift** | `CLAUDE.md` fences off sensitive paths while Cursor rules say agents may edit any file. |\n| **MCP drift** | The same MCP server is pinned in one config, `@latest` in another, and disabled somewhere else. |\n| **Permission drift** | Claude denies a sensitive read while Codex is trusted with network access and a broader sandbox. |\n| **Operational hazards** | A config launches an MCP server through `sudo`, points at a broken local script, or hides a risky imperative in review noise. |\n\n## Quickstart\n\n### As a GitHub Action (most common)\n\n```yaml\nname: PolicyMesh\non: pull_request\npermissions:\n  contents: read\n\njobs:\n  policymesh:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v6\n        with:\n          fetch-depth: 0     # required for diff mode\n      - uses: Conalh/PolicyMesh@v0.5.2\n        with:\n          fail-on: high\n          diff: true         # gate only on findings this PR introduces or worsens\n```\n\nWrites a Markdown report to the Actions step summary and emits PR-visible `::warning` annotations on the exact conflicting config lines.\n\n### Local CLI\n\n```bash\ngit clone https://github.com/Conalh/PolicyMesh\ncd PolicyMesh\nnpm install\nnpm run build\n\n# Audit the bundled conflicted fixture\nnode dist/index.js audit --repo test/fixtures/conflicted --format markdown\n\n# Or audit a real repo\nnode dist/index.js audit --repo /path/to/your/repo --format text\n```\n\n## Example output\n\nReal output from `test/fixtures/conflicted`, `--format text`:\n\n```\nPolicyMesh agent policy review: HIGH\n\nEffective capability union:\n- 1 MCP server configured\n- 3 unpinned MCP packages\n- bash wildcards allowed (Claude)\n- broad read paths allowed (Claude)\n- network enabled (Codex)\n- Codex project trusted\n- Codex sandbox: workspace-write\n- Strictest: Claude (1 sensitive deny rule) · Loosest: Codex (trusted + network)\n\n[HIGH]   github: MCP server \"github\" has different launch commands across surfaces:\n         \"npx -y @modelcontextprotocol/server-github@1.2.3\" vs \"@latest\" vs \"@2.0.0\".\n         Surfaces: Root MCP, Cursor MCP, VS Code MCP, Windsurf MCP, Codex.\n[MEDIUM] github: unpinned command across 3 surfaces (@latest). Surfaces: Cursor, VS Code, Windsurf.\n[MEDIUM] Read(.env): Claude denies Read(.env) but has broad allow rules Bash(npm *), Read(~/**).\n[MEDIUM] network_access: Codex network access enabled alongside other configured surfaces.\n[HIGH]   github: Codex project trusted while MCP servers are unpinned and inconsistent.\n```\n\n`--format json` emits the canonical [agent-gov-core](https://github.com/Conalh/agent-gov-core) `Report` envelope — the same shape every tool in the suite emits, so GovVerdict can merge them:\n\n```json\n{\n  \"schemaVersion\": \"1.0\",\n  \"tool\": \"policy_mesh\",\n  \"rating\": \"high\",\n  \"findings\": [\n    {\n      \"tool\": \"policy_mesh\",\n      \"kind\": \"policy_mesh.mcp_command_mismatch\",\n      \"severity\": \"high\",\n      \"message\": \"MCP server \\\"github\\\" has different launch commands across surfaces…\",\n      \"location\": { \"file\": \".mcp.json\", \"line\": 3 },\n      \"salientKey\": \"github\",\n      \"data\": {\n        \"subject\": \"github\",\n        \"recommendation\": \"Use the same pinned MCP server definition in every MCP config file.\",\n        \"surfaces\": [\"root_mcp\", \"cursor_mcp\", \"vscode_mcp\", \"windsurf_mcp\", \"codex\"],\n        \"signature\": \"d0bb4972fd9e855d\"\n      },\n      \"fingerprint\": \"ce65620cb8140af3\"\n    }\n  ]\n}\n```\n\n`--format sarif` is also supported for the GitHub Security tab and other SAST consumers.\n\n## How it works\n\n- Runs against the **checked-out repo** — no upload, no hosted scanner, no telemetry. The GitHub Action writes a Markdown report to the step summary and emits PR-visible annotations; pass `github-token` to additionally post a sticky PR comment that updates in place.\n- One audit pass renders five output formats: `text` for terminals, `markdown` for step summaries and PR comments, `json` for piping to GovVerdict, `github` for `::warning` annotations on the exact conflicting line, `sarif` for the GitHub Security tab.\n- Detectors group by canonical identity (for example, MCP command normalization ignores neutral flag reordering / `-y` vs `--yes` / `.cmd` vs `.exe`) and fire only when two or more surfaces actually disagree.\n- **Diff mode** (`diff: true`) audits the PR base in a temporary worktree, audits HEAD, and gates only on **new or worsened** findings — so a PR does not fail on pre-existing conflicts. Findings resolved by the PR are surfaced separately as green-check signal.\n- **`fix` / `fix pin`** can auto-align MCP enabled-state or `command` / `args` drift to a canonical surface you nominate. Always dry-run first; `--write` does line-targeted edits that preserve comments and indentation.\n- **Baselines.** `.policymesh-exceptions.json` suppresses known-and-documented findings, optionally locked to a content signature so the suppression breaks if the violation later changes. `.policymesh-baseline.json` encodes the positive state the team requires and fires HIGH on drift.\n\n## Design choices worth flagging\n\n- **Cross-surface by default.** A single policy file is rarely the full truth; the useful signal is where two tools expose incompatible rules to the same agent workflow.\n- **Adoptable in messy repos.** Diff mode gates on new or worsened findings, which lets teams start advisory and tighten later without fixing all historical drift first.\n- **One report model.** CLI text, Markdown, GitHub annotations, JSON, and SARIF all render from the same report object, so the machine-readable and human-readable surfaces stay aligned.\n- **Narrow fixes.** The `fix` commands are dry-run-first and line-targeted because policy edits should remain reviewable instead of becoming a silent rewrite of repo governance.\n\n## Options\n\n### CLI\n\n| Command | What it does |\n| --- | --- |\n| `policymesh audit --repo \u003cpath\u003e` | Full repo audit. `--format text\\|markdown\\|json\\|github\\|sarif`. `--recursive` for monorepos. |\n| `policymesh diff --base-ref \u003cgit-ref\u003e` | Audit a base ref in a temp worktree, audit working tree, print the delta. |\n| `policymesh diff --base-report a.json --head-report b.json` | Diff two saved JSON audits. |\n| `policymesh fix --canonical \u003csurface\u003e [--write]` | Align MCP enabled / disabled state to a canonical surface. |\n| `policymesh fix pin --canonical \u003csurface\u003e [--write]` | Align MCP `command` / `args` to a canonical surface. |\n| `policymesh render --input \u003cjson\u003e --format \u003cfmt\u003e` | Re-render a saved audit in another format. |\n\n`\u003csurface\u003e` is one of: `root_mcp`, `cursor_mcp`, `vscode_mcp`, `codeium_mcp`, `windsurf_mcp`, `claude`, `codex`, `aider`, `instructions`.\n\n### GitHub Action inputs\n\n| Input | Default | Purpose |\n| --- | --- | --- |\n| `repo` | `$GITHUB_WORKSPACE` | Checkout path to inspect. |\n| `fail-on` | `none` | Severity that fails the step: `none`, `low`, `medium`, `high`, `critical`. Start advisory, raise later. |\n| `diff` | `false` | On `pull_request`, gate only on findings introduced or worsened by this PR. |\n| `recursive` | `false` | Monorepo mode — audit every sub-project with its own agent config independently. |\n| `github-token` | _(unset)_ | Optional `GITHUB_TOKEN` with `pull-requests: write` to post a sticky PR comment that updates in place. |\n\n### GitHub Action outputs\n\n`rating` (`none`/`low`/`medium`/`high`/`critical`), `finding-count`, `surface-count`.\n\n## Detection coverage\n\nPolicyMesh v0.5 detects:\n\n- **Instruction drift:** risky imperatives in `AGENTS.md`, `CLAUDE.md`, `.cursor/rules/*.md`, and `.github/copilot-instructions.md` such as \"ignore deny rules\", \"edit any file\", or \"auto-commit\".\n- **MCP drift:** command mismatches, missing-server gaps, enabled-state drift, env / header drift without echoing secret values, unpinned `@latest` packages, and hardcoded API credentials in MCP launch lines.\n- **Permission drift:** Claude broad-allow vs narrow-deny contradictions, Claude broad allows without a `PreToolUse` hook, Claude MCP grants for servers that are not configured, Codex network-access + trusted-project + risky-MCP combinations, Codex sandbox gaps relative to Claude denies, and Aider `dangerously-allow-non-git`.\n- **Operational hazards:** MCP servers launched via elevation utilities (`sudo`, `pkexec`, `runas`…), broken local script paths, and config parser differences that normally hide in review noise.\n\nVS Code and Cursor configs are parsed as JSONC, so comments and trailing commas are accepted.\n\n## How well it catches it\n\nPolicyMesh ships a labeled precision/recall benchmark over **31 fixture repos** (24 with planted drift, 7 clean) spanning **24 detector kinds**. Ground truth is fixed by fixture design; the harness scores the audit engine against it. Reproduce with `npm run build \u0026\u0026 node benchmark/run-benchmark.mjs`.\n\n| Metric | Result |\n| --- | --- |\n| Detection (any finding) — recall | **100%** (24/24 rogue repos flagged) |\n| Detection — false-positive rate | **0%** (0/7 clean repos flagged) |\n| Detection — precision | **100%** |\n| Correct primary finding kind | **24/24** rogue repos |\n| All expected finding kinds | **24/24** rogue repos |\n| Exact consolidated rating | **21/21** repos where the label pins one |\n\nThe 7 clean repos include four engineered **false-positive traps** — neutral `npx -y` vs `npx`, multi-line Codex TOML `args`, Aider `dangerously-allow-non-git: false`, and benign \"Always/Never\" prose — plus a baseline-satisfied repo and an active exception. None produce a finding.\n\n**Severity is calibrated, not maximized.** At a strict `fail-on: high` gate, recall is 54% — by design: env / header / enabled-state drift and missing servers are medium-or-low because they are reproducibility hazards, not exploits. The critical band is reserved for hardcoded secrets. Full confusion matrix at every gate, per-category and per-case breakdowns: [benchmark/RESULTS.md](benchmark/RESULTS.md). Methodology and labels: [benchmark/labels.json](benchmark/labels.json).\n\n## Part of the agent-gov suite\n\nLocal-only OSS tools that review AI-agent PRs and coding sessions for config drift, policy mismatches, and scope creep. Each tool covers an orthogonal failure mode; they share a canonical `Finding` schema and can be merged into a single verdict.\n\n| Repo | What it catches |\n| --- | --- |\n| **[ScopeTrail](https://github.com/Conalh/ScopeTrail)** | Diffs agent config files between PR base and head — permission drift. |\n| **PolicyMesh** *(this repo)* | Finds contradictory agent instructions and config drift that make behavior non-reproducible. |\n| **[CapabilityEcho](https://github.com/Conalh/CapabilityEcho)** | Network, subprocess, eval, lifecycle, and workflow-permission signals in code diffs. |\n| **[TaskBound](https://github.com/Conalh/TaskBound)** | Compares the stated task to the actual diff — scope creep. |\n| **[SessionTrail](https://github.com/Conalh/SessionTrail)** | Parses Cursor / Claude / Codex JSONL session transcripts for runtime behavior. |\n| **[GovVerdict](https://github.com/Conalh/GovVerdict)** | Merges JSON reports from the tools above into a single verdict. |\n| **[agent-gov-core](https://github.com/Conalh/agent-gov-core)** | Shared parsers, the canonical `Finding` schema, `mergeFindings`. |\n| **[agent-gov-demo](https://github.com/Conalh/agent-gov-demo)** | Sandbox repo with a rogue PR that exercises all five tools end-to-end. |\n\n**Demo PR exercising the full stack:** [agent-gov-demo#1](https://github.com/Conalh/agent-gov-demo/pull/1)\n\n---\n\nMIT. Bug reports and false-positive reports welcome via [Issues](https://github.com/Conalh/PolicyMesh/issues).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconalh%2Fpolicymesh","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fconalh%2Fpolicymesh","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fconalh%2Fpolicymesh/lists"}