Directory layout
.harness/
|-- manifest.yaml
|-- roles/
|-- guardrails/
| |-- safety.yaml
| `-- conventions.yaml
`-- knowledge/
Policy reference
Guardrails are the safety layer between agent intent and target repo mutation. They make policy explicit: which roles receive which warnings, which writes are blocked, which secret patterns stop commits, and how operators review or retire policy over time.
MARS uses guardrails in two complementary ways. Repo-owned YAML rules define target-specific policy, while built-in runtime policy blocks unsafe tool behavior such as secret leakage, dangerous shell commands, dirty generated output, and unreviewable blast radius.
| Layer | User controls | Effect |
|---|---|---|
| Advisory rules | severity: advisory | Added to role context. The model is warned, but the rule does not mechanically block a write. |
| Hard rules | severity: hard with a content pattern | Checked by the guardrails engine and blocks matching file content for matching roles. |
| Secret scanning | mars guardrails secret-scan and optional hook | Finds common credential patterns with redacted output and exits non-zero on findings. |
| Runtime policy | Tool allowlists, trust, and built-in policy checks | Prevents unsafe tool calls and oversized or untracked mutations before they become accepted work. |
.harness/guardrails/. Leave general tool safety,
shell safety, and workspace hygiene to the built-in policy layer.
Guardrail files live inside the target harness and are attached per
role from .harness/manifest.yaml. A role only receives
the guardrail files listed on that role.
.harness/
|-- manifest.yaml
|-- roles/
|-- guardrails/
| |-- safety.yaml
| `-- conventions.yaml
`-- knowledge/
roles:
engineer:
prompt: roles/engineer.md
tools: [file_read, file_write, shell_exec, grep]
guardrails:
- guardrails/safety.yaml
- guardrails/conventions.yaml
mars run engineer --repo /path/to/target-repo --dry-run
mars guardrails secret-scan --repo /path/to/target-repo --staged
Use dry-run to see assembled advisory context before calling a model.
A guardrail file has a top-level rules list. MARS
defaults missing scope to global, missing
severity to advisory, and missing creation
time to the time the file is loaded.
rules:
- id: no-hardcoded-secrets
name: No hardcoded secrets
severity: hard
scope: global
pattern: '(?i)(password|secret|api_key|token)\s*[:=]\s*["''][^"'']{8,}'
file_pattern: '*.go'
message: Do not hardcode secrets. Use environment variables or local secret storage.
stale_days: -1
| Field | Required? | User behavior |
|---|---|---|
id | Yes | Stable machine-readable identifier. Use lower-kebab-case and do not reuse it for a different policy. |
name | Yes | Short human label shown in violations and advisory context. |
severity | No | advisory or hard. Missing severity defaults to advisory. |
scope | No | global or a role name. Missing scope defaults to global. |
pattern | No | RE2-compatible content regex. Invalid regex fails closed when rules are loaded. |
file_pattern | No | Glob matched against the file basename, such as *.go or *.sql. |
message | Yes | Actionable remediation shown to agents and operators. |
stale_days | No | 0 or omitted uses the default review window. A negative value means never stale. |
Understanding matching prevents surprise blocks and false confidence. Guardrails are intentionally syntactic in the current implementation: regex content checks, role scope, and basename file glob checks.
scope: global applies to every role that loads the file. A role name such as engineer applies only to that role.
pattern is compiled before execution. Invalid regexes reject the rule file instead of running under unknown policy.
file_pattern uses basename matching. *.go matches internal/app/main.go; path-prefix matching is not part of this v1 rule engine.
A hard rule blocks when the role scope matches, the file glob matches, and the content regex matches. A hard rule without pattern is not useful as a file-content blocker.
Advisory rules are deduplicated by id and inserted once into the role context for matching roles.
AST-aware checks, semantic validation, and path-prefix predicates belong to future or built-in policy surfaces, not the YAML rule matcher.
rules:
- id: no-hardcoded-secrets
name: No hardcoded secrets
severity: hard
scope: global
pattern: '(?i)(password|secret|api_key|token)\s*[:=]\s*["''][^"'']{8,}'
message: Do not hardcode secrets. Use environment variables or .harness/.env.local.
rules:
- id: no-drop-table
name: No destructive migrations
severity: hard
scope: engineer
file_pattern: '*.sql'
pattern: '(?i)\bDROP\s+TABLE\b'
message: Destructive migrations need explicit human approval and a rollback plan.
rules:
- id: public-api-compatibility
name: Preserve public API
severity: advisory
scope: reviewer
message: Check exported names, CLI flags, config keys, and documented behavior before approval.
mars guardrails secret-scan scans common credential
shapes and returns a blocking exit code when findings exist. Output
includes file, line, and pattern name; the matched value is
redacted.
mars guardrails secret-scan --repo /path/to/target-repo
mars guardrails secret-scan --repo /path/to/target-repo --staged
mars guardrails secret-scan --repo /path/to/target-repo --json
| Pattern class | Examples caught | Notes |
|---|---|---|
| AWS access key | AKIA... | Common long-lived AWS key prefix. |
| GitHub token | ghp_, gho_, ghs_ | Matched value is redacted in text and JSON output. |
| Private key block | RSA, DSA, EC, OpenSSH, or PGP private key header. | Treat as leaked until rotated. |
| Password in URL | Credentials embedded before @. | Remove the credential from source and history as needed. |
| Generic API key assignment | api_key, secret_key, access_token. | Review false positives carefully; do not commit real values. |
The scanner skips .git/ and the ignored local secret
file .harness/.env.local. Commit only
.harness/.env.example with environment variable names,
not values.
The hook installer adds an idempotent MARS-managed block to the target repo's pre-commit hook. The hook runs staged secret scanning before a local commit completes.
mars guardrails install-hooks --repo /path/to/target-repo
mars guardrails install-hooks --repo /path/to/target-repo --json
mars guardrails secret-scan --repo /path/to/target-repo --staged
Install it for local developer safety. Keep CI or release gates separate if your team needs server-side enforcement.
Guardrails should age on purpose. A stale rule is not necessarily wrong; it is a signal to review whether the policy still describes the current product, team, and agent behavior.
| Setting | Meaning | Use it for |
|---|---|---|
| Omitted | Default review window. | Most rules. |
stale_days: 0 | Default review window. | Equivalent to omission in the current engine. |
stale_days: 30 | Review after 30 days from creation/load time. | Temporary conventions and newly tuned policies. |
stale_days: -1 | Never stale. | Permanent safety rules such as no committed secrets. |
If a stale rule still matters, keep it and refresh the rationale in
the rule message or adjacent docs. If it produces false positives,
narrow the regex, add a file_pattern, change it to
advisory, or remove it with a commit that explains why.
The guardrails engine has an override model for active hard-rule
exemptions, but users should treat overrides as exceptional. There
is no general mars guardrails bypass command in the
public guardrails CLI. Prefer fixing the violation or narrowing an
overbroad rule in git.
The rule is correct, but a one-off human-approved operation needs a time-bounded exception.
The rule fires often during normal work. That means the rule should be refined or converted to advisory.
Record who approved the exception, which rule ID was bypassed, why, and when the exception expires.
Commit a narrower policy change and release notes so future agents inherit the corrected behavior.
Use severity: advisory while observing whether the instruction helps roles make better choices.
Use severity: hard for syntactic checks with a reliable regex and a clear remediation path.
List the rule file only on roles that need it, and use role-specific scope when possible.
mars run engineer --repo /path/to/target-repo --dry-run
mars guardrails secret-scan --repo /path/to/target-repo --staged
mars doctor --repo /path/to/target-repo --json
Guardrail changes are repo-owned behavior. Commit the YAML and any affected docs together.
| Symptom | Likely cause | Next action |
|---|---|---|
| Role ignores a rule | The role does not list that guardrail file, or the rule is scoped to another role. | Check .harness/manifest.yaml and the rule scope. |
| Hard rule never blocks | The rule has no pattern, the file basename misses file_pattern, or the regex does not match. | Test the regex, remove over-narrow file globbing, or make it advisory. |
| Rule file fails to load | Invalid YAML or invalid regex. | Fix the parse error before running agents; invalid policy should fail closed. |
| Secret scan blocks commit | A staged or repo file matches a credential pattern. | Remove the value, rotate it if real, and rerun mars guardrails secret-scan --staged. |
| False positive in secret scan | A test fixture or placeholder looks like a secret. | Use safer placeholder strings. Do not weaken real secret patterns casually. |
| Hook install fails | The path is not a git checkout or the hook file is not writable. | Run from the target repo, fix permissions, then rerun install-hooks. |
| Rule feels too broad | Content regex is too general or applies globally. | Add file_pattern, scope it to one role, or make it advisory first. |