lunar-policy
Assists in building policy plugins to enforce engineering standards via Lunar.
Install
mkdir -p .claude/skills/lunar-policy && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/17056" && unzip -o skill.zip -d .claude/skills/lunar-policy && rm skill.zipInstalls to .claude/skills/lunar-policy
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Create Lunar policy plugins that enforce engineering standards. Use when building policies (Python scripts) that evaluate Component JSON data and produce pass/fail checks. Covers the lunar_policy SDK (Check class, assertions, Node navigation), enforcement levels, handling missing data, plugin structure, and testing patterns.Key capabilities
- →Read data from Component JSON
- →Make assertions about data using `Check` class
- →Produce pass/fail/pending/error/skipped checks
- →Configure policy plugins with `lunar-policy.yml`
- →Handle missing data using `assert_exists` or `exists` checks
- →Define configurable inputs for policies
How it works
This skill enables the creation of Python-based policy plugins that evaluate Component JSON data using the `lunar_policy` SDK's `Check` class and assertion methods. Policies read data, make assertions, and produce checks with various statuses.
Inputs & outputs
When to use lunar-policy
- →Build engineering guardrails
- →Write custom policy checks
- →Enforce codebase compliance
- →Automate project audits
About this skill
Lunar Policy Skill
Create policy plugins for Earthly Lunar—Python scripts that evaluate Component JSON data and produce pass/fail checks for guardrail enforcement.
Quick Start
- Read about-lunar.md for platform overview
- Read core-concepts.md for architecture and key entities
- Read policy-reference.md for comprehensive policy documentation
Policy Basics
A policy is a Python script that:
- Reads data from the Component JSON
- Makes assertions about that data
- Produces checks (pass/fail/pending/error/skipped)
from lunar_policy import Check
with Check("readme-exists", "Repository should have a README.md") as c:
c.assert_true(c.get_value(".repo.readme_exists"), "README.md not found")
Plugin Structure
my-policy/
├── lunar-policy.yml # Required: plugin config
├── check_one.py # One file per check
├── check_two.py
├── requirements.txt # Must include lunar-policy
├── Dockerfile # Optional: for custom dependencies
└── README.md # Documentation
lunar-policy.yml:
version: 0
name: my-policy
description: Validates X requirements
author: [email protected]
default_image: earthly/lunar-scripts:1.0.0
policies:
- name: check-one
description: Validates X exists
mainPython: check_one.py
- name: check-two
description: Ensures Y meets threshold
mainPython: check_two.py
inputs:
min_coverage:
description: Minimum required coverage
default: "80"
requirements.txt:
lunar-policy==0.2.2
The Check Class
Data Access
# Get value (raises NoDataError if missing → pending)
readme_exists = c.get_value(".repo.readme_exists")
# Get value with default (never goes pending)
replicas = c.get_value_or_default(".k8s.replicas", 1)
# Check existence
if c.exists(".coverage"):
# Data is available
# Get node for navigation
k8s = c.get_node(".k8s.workloads")
for workload in k8s:
name = workload.get_value(".name")
Assertions
c.assert_true(value, "Failure message")
c.assert_false(value, "Failure message")
c.assert_equals(value, expected, "Failure message")
c.assert_exists(".path", "Failure message") # Path must exist
c.assert_contains(list_or_str, item, "Failure message")
c.assert_greater(value, threshold, "Failure message")
c.assert_greater_or_equal(value, threshold, "Failure message")
c.assert_match(value, r"pattern", "Failure message")
c.fail("Unconditional failure")
c.skip("Check doesn't apply") # Only for applicability filtering
Enforcement Levels
| Level | PR Comments | Blocks PR | Blocks Release |
|---|---|---|---|
draft | No | No | No |
score | No | No | No |
report-pr | Yes | No | No |
block-pr | Yes | Yes | No |
block-release | No | No | Yes |
block-pr-and-release | Yes | Yes | Yes |
Handling Missing Data
Pattern 1: Required data with assert_exists
c.assert_exists(".coverage", "Coverage data not found")
coverage = c.get_value(".coverage.percentage")
Pattern 2: Optional data with exists check
if c.exists(".coverage"):
coverage = c.get_value(".coverage.percentage")
c.assert_greater_or_equal(coverage, 80, "Coverage too low")
Pattern 3: Object presence = signal (CI collectors)
# SCA scanner only writes data when it runs
c.assert_exists(".sca", "SCA scanner must run in CI")
Configurable Inputs
from lunar_policy import Check, variable_or_default
min_coverage = int(variable_or_default("minCoverage", "80"))
c.assert_greater_or_equal(coverage, min_coverage, f"Coverage {coverage}% below {min_coverage}%")
Reference Documentation
For detailed information, read these files in the references/ directory:
| File | Content |
|---|---|
| about-lunar.md | Platform overview, why Lunar exists |
| core-concepts.md | Architecture, Component JSON, enforcement levels |
| policy-reference.md | Complete policy guide - Check class API, patterns, testing |
| component-json/conventions.md | Component JSON schema conventions, presence detection, boolean vs object patterns |
| component-json/structure.md | Component JSON schema categories (.repo, .k8s, .sca, etc.) with policy paths |
| strategies.md | Implementation strategies |
| policy-README-template.md | README template for policy plugins |
Hosted Documentation Backup
The references/ files above are the primary source. Only if they do not answer the question, fetch https://docs-lunar.earthly.dev/llms.txt to find the relevant hosted markdown page.
If a page still lacks enough context, ask the docs a specific, self-contained question with ?ask=<question> on that page URL, for example:
GET https://docs-lunar.earthly.dev/readme.md?ask=How%20do%20I%20configure%20policy%20enforcement%3F
Local Development & Testing
Run policies locally to test before deploying. Commands must be run from a directory containing lunar-config.yml.
Prerequisites:
- Set
LUNAR_HUB_TOKENenvironment variable for authentication - Be in a directory with a valid
lunar-config.yml
Run a policy with Component JSON from a file:
lunar policy dev <policy-name> --verbose --component-json path/to/component.json
Run a policy with Component JSON from stdin:
lunar policy dev <policy-name> --verbose --component-json -
Run a policy against a remote component:
lunar policy dev <policy-name> --verbose --component github.com/org/repo
Policy names are dot-separated (e.g., k8s.pdb, container.no-latest).
End-to-end testing by piping collector output to policy:
# Against a local repo directory
lunar collector dev my-collector --component-dir ../path/to/repo | \
lunar policy dev my-policy --verbose --component-json -
# Against a remote component
lunar collector dev my-collector --component github.com/org/repo | \
lunar policy dev my-policy --verbose --component-json -
The command outputs check results as text showing pass/fail status and failure messages.
Exit codes: Zero on success (even if checks fail—policy ran correctly), non-zero only on errors (e.g., invalid syntax, missing dependencies, uncaught exception).
Best Practices
- One check per policy entry - Enables selective
include/exclude - Descriptive check names and messages - Include context in failures
- Use
assert_existsfor required data - Notget_value_or_default - Handle missing data appropriately - See patterns above
- Keep policies fast - No external API calls (use collectors instead)
- Use
earthly/lunar-scripts:1.0.0- Official base image
Common Patterns
Iterating over collections:
for workload in c.get_node(".k8s.workloads"):
name = workload.get_value_or_default(".name", "<unknown>")
for container in workload.get_node(".containers"):
has_resources = container.get_value_or_default(".has_resources", False)
c.assert_true(has_resources, f"{name}: container missing resource limits")
Allow-list validation (error if empty):
allowed_str = variable_or_default("allowed_registries", "")
allowed = [r.strip() for r in allowed_str.split(",") if r.strip()]
if not allowed:
raise ValueError("'allowed_registries' must be configured")
if registry not in allowed:
c.fail(f"Registry '{registry}' not in allowed list")
Testable policy function:
from lunar_policy import Check, Node, CheckStatus
def check_readme(node=None):
c = Check("readme-exists", node=node)
with c:
c.assert_true(c.get_value(".repo.readme_exists"), "README not found")
return c
if __name__ == "__main__":
check_readme()
# Test:
def test_readme_passes():
node = Node.from_component_json({"repo": {"readme_exists": True}})
check = check_readme(node)
assert check.status == CheckStatus.PASS
When not to use it
- →When external API calls are needed within policies
Prerequisites
Limitations
- →Policies should not make external API calls.
- →The skill requires `lunar-policy.yml` for plugin configuration.
- →The skill requires `requirements.txt` to include `lunar-policy`.
How it compares
This workflow provides a structured, code-based approach to defining and enforcing engineering standards through automated checks on Component JSON data, offering a programmatic and auditable guardrail system compared to manual code reviews
Compared to similar skills
lunar-policy side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| lunar-policy (this skill) | 0 | 2mo | Review | Advanced |
| engineering-skills | 4 | 2mo | Review | Intermediate |
| azure-functions | 10 | 5mo | Review | Intermediate |
| ml-pipeline-workflow | 9 | 5mo | No flags | Advanced |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
engineering-skills
alirezarezvani
23 production-ready engineering skills covering architecture, frontend, backend, fullstack, QA, DevOps, security, AI/ML, data engineering, computer vision, and specialized tools like Playwright Pro, Stripe integration, AWS, and MS365. 30+ Python automation tools (all stdlib-only). Works with Claude Code, Codex CLI, and OpenClaw.
azure-functions
aj-geddes
Create serverless functions on Azure with triggers, bindings, authentication, and monitoring. Use for event-driven computing without managing infrastructure.
ml-pipeline-workflow
wshobson
Build end-to-end MLOps pipelines from data preparation through model training, validation, and production deployment. Use when creating ML pipelines, implementing MLOps practices, or automating model training and deployment workflows.
cookbook-audit
anthropics
Audit an Anthropic Cookbook notebook based on a rubric. Use whenever a notebook review or audit is requested.
pr-review
pytorch
Review PyTorch pull requests for code quality, test coverage, security, and backward compatibility. Use when reviewing PRs, when asked to review code changes, or when the user mentions "review PR", "code review", or "check this PR".
machine-learning-ops-ml-pipeline
sickn33
Design and implement a complete ML pipeline for: $ARGUMENTS