Decision File Format Reference
March 14, 2026 · View on GitHub
Author: Ali Abbas
Project: Decispher
Complete reference for the decisions file format used by Decision Guardian.
Table of Contents
- File Location
- Decision Structure
- Required Fields
- Optional Fields
- File Patterns
- Advanced Rules
- Content Matching
- Examples
- Validation
File Location
Default: .decispher/decisions.md
Customizable via:
- GitHub Action:
decision_fileinput - CLI: positional argument to
check <path>, or auto-discovered bycheckall
Accepted values:
- Single file:
.decispher/decisions.md - Directory:
.decispher/(auto-discovers all.mdfiles recursively)
Directory scanning:
- Recursively finds all
.mdfiles - Skips hidden directories (
.git,.github) - Merges all decisions into single collection
Decision Structure
Basic Format
<!-- DECISION-ID -->
## Decision: Title
**Status**: Active
**Date**: YYYY-MM-DD
**Severity**: Critical|Warning|Info
**Files**:
- `pattern`
### Context
Explanation of the decision.
---
Required Elements
- Decision Marker:
<!-- DECISION-ID --> - Title:
## Decision: Title - At least one of:
FilesorRules
All other fields have defaults if omitted.
Required Fields
Decision ID
<!-- DECISION-[IDENTIFIER] -->
Format rules:
- Must start with
DECISION- - Followed by uppercase letters, numbers, and hyphens
- Case-insensitive (normalized to uppercase)
Valid examples:
<!-- DECISION-001 -->
<!-- DECISION-DB-001 -->
<!-- DECISION-API-AUTH-001 -->
<!-- DECISION-TEST-001 -->
Title
## Decision: Your Decision Title
Guidelines:
- Clear and descriptive
- Explains what the decision covers
- Keep under 100 characters
Optional Fields
Status
**Status**: Active
| Status | Synonyms | Behavior |
|---|---|---|
active | enabled, live | ✅ Triggers PR alerts |
deprecated | obsolete | ⚠️ Parsed but ignored |
superseded | replaced | ⚠️ Parsed but ignored |
archived | inactive | ⚠️ Parsed but ignored |
Default: active
Important: Only active decisions trigger alerts.
Date
**Date**: 2024-01-15
Format: ISO 8601 (YYYY-MM-DD)
Validation:
- Future dates trigger warning
- Dates >10 years old suggest review
Default: Current date
Severity
**Severity**: Critical
| Severity | Synonyms | Display | Fails check? |
|---|---|---|---|
info | informational, low | ℹ️ | No |
warning | warn, medium | 🟡 | No |
critical | error, high, blocker | 🔴 | Yes (if fail_on_critical: true / --fail-on-critical) |
Default: info
File Patterns
Basic Syntax
**Files**:
- `src/db/pool.ts`
- `src/**/*.ts`
- `config/*.yml`
Glob Patterns
| Pattern | Matches | Example |
|---|---|---|
file.ts | Exact file | src/app.ts |
*.ts | All .ts in directory | src/*.ts |
**/*.ts | All .ts recursively | src/**/*.ts |
{a,b} | a or b | *.{ts,js} |
[a-z] | Character range | test[0-9].ts |
!pattern | Exclusion | !**/*.test.ts |
Format Options
Both backtick formats supported:
**Files**:
- `src/app.ts` <!-- recommended -->
- src/utils.ts <!-- also works -->
Examples
**Files**:
- `package.json` # Exact file
- `src/**/*.ts` # All TypeScript
- `config/*.{yml,yaml}` # Config files
- `migrations/**/*.sql` # SQL migrations
- `!migrations/**/test_*.sql` # Exclude tests
Advanced Rules
JSON-based rules for fine-grained control.
Inline Rules
**Rules**:
```json
{
"type": "file",
"pattern": "src/**/*.ts",
"content_rules": [
{
"mode": "string",
"patterns": ["TODO", "FIXME"]
}
]
}
### External Rules
```markdown
**Rules**: ./rules/security.json
or
**Rules**: [Security Rules](./rules/security.json)
External file example:
{
"match_mode": "any",
"conditions": [
{
"type": "file",
"pattern": "src/**/*.ts"
}
]
}
Rule Structure
Top-Level
{
"match_mode": "any" | "all", // OR | AND
"conditions": [...] // Array of rules
}
| Property | Type | Default | Description |
|---|---|---|---|
match_mode | "any" | "all" | "any" | Boolean logic for conditions |
conditions | Array | Required | List of file rules or nested conditions |
File Rule
{
"type": "file",
"pattern": "src/**/*.ts", // Glob pattern
"exclude": "**/*.test.ts", // Optional exclusion
"content_match_mode": "any", // Optional: "any" (OR, default) | "all" (AND)
"content_rules": [...] // Optional content matching
}
| Property | Type | Required | Description |
|---|---|---|---|
type | "file" | Yes | Must be "file" |
pattern | String | Yes | Glob pattern for file matching |
exclude | String | No | Glob pattern to exclude files |
content_match_mode | "any" | "all" | No | How content_rules are combined. "any" (default) fires if any rule matches; "all" fires only when every rule matches |
content_rules | Array | No | Rules to match within file diff |
Content Matching
Rules for matching within file diffs.
Common Properties
All content rules (except full_file) support these optional boolean properties to control exactly which parts of a git diff are searched:
| Property | Type | Default | Description |
|---|---|---|---|
match_changed_lines_only | Boolean | false | By default, matchers search the entire file if it was modified. Set to true to restrict the search strictly to the lines that were changed in the diff. |
match_deleted_lines | Boolean | false | By default, only added (+) lines in the diff are searched. Set to true to also search lines that were removed (-). Useful for catching when a developer deletes a security guardrail. |
1. String Mode
Match exact strings in changed lines.
{
"mode": "string",
"patterns": ["pool_size", "max_connections"]
}
Use case: Simple keyword detection
| Property | Type | Description |
|---|---|---|
mode | "string" | Required |
patterns | String[] | List of strings to search for |
2. Regex Mode
Pattern matching with security protection.
{
"mode": "regex",
"pattern": "MAX_\\w+\\s*=\\s*\\d+",
"flags": "i"
}
Flags: i (case-insensitive), m (multiline), g (global)
| Property | Type | Description |
|---|---|---|
mode | "regex" | Required |
pattern | String | Regular expression pattern |
flags | String | Optional regex flags (i = case-insensitive) |
Security:
- 5-second timeout
- ReDoS detection (safe-regex)
- VM sandbox execution
Use case: Complex pattern matching
3. Line Range Mode
Match changes in specific line numbers.
{
"mode": "line_range",
"start": 15,
"end": 45
}
Use case: Protect license headers, config blocks
| Property | Type | Description |
|---|---|---|
mode | "line_range" | Required |
start | Number | Start line number (inclusive) |
end | Number | End line number (inclusive) |
4. Full File Mode
Match any change to the file.
{
"mode": "full_file"
}
Use case: Files always needing review
5. JSON Path Mode
Match JSON keys in changes.
{
"mode": "json_path",
"paths": ["$.database.pool_size", "$.cache.ttl"]
}
Use case: Config files where only certain fields matter
| Property | Type | Description |
|---|---|---|
mode | "json_path" | Required |
paths | String[] | JSON paths to check |
Boolean Logic
OR Logic (match_mode: "any")
{
"match_mode": "any",
"conditions": [
{ "type": "file", "pattern": "Dockerfile" },
{ "type": "file", "pattern": "docker-compose.yml" }
]
}
Triggers if either file changes.
AND Logic (match_mode: "all")
{
"match_mode": "all",
"conditions": [
{ "type": "file", "pattern": "src/auth/**/*.ts" },
{ "type": "file", "pattern": "config/auth.yml" }
]
}
Triggers only if both change.
AND Logic within a single file rule (content_match_mode: "all")
Use content_match_mode: "all" on a FileRule to require that every entry in content_rules matches (AND). The default is "any" (OR — fires if at least one rule matches).
{
"type": "file",
"pattern": "src/api/**/*.ts",
"content_match_mode": "all",
"content_rules": [
{ "mode": "string", "patterns": ["router.post("] },
{ "mode": "regex", "pattern": "authMiddleware" }
]
}
Triggers only when a changed file both contains router.post( and matches authMiddleware.
Nested Logic
Maximum depth: 10 levels
{
"match_mode": "all",
"conditions": [
{
"match_mode": "any",
"conditions": [
{ "type": "file", "pattern": "config/db.yml" },
{ "type": "file", "pattern": "config/db.json" }
]
},
{ "type": "file", "pattern": "src/db/**/*.ts" }
]
}
Logic: (config/db.yml OR config/db.json) AND src/db/**/*.ts
Context Section
Free-form markdown describing the decision.
### Context
The connection pool size in `database.yml` is set to 20 connections.
**Why this matters:**
- Database has 100 connection limit
- 5 services × 20 = 100 (at capacity)
**References:**
- [Incident Report](https://wiki.example.com/incident-456)
- [Architecture Doc](https://docs.example.com/db-design)
Examples
Example 1: Simple File Patterns
<!-- DECISION-DB-001 -->
## Decision: Database Pool Configuration
**Status**: Active
**Date**: 2024-01-15
**Severity**: Critical
**Files**:
- `src/db/pool.ts`
- `config/database.yml`
### Context
Pool size fixed at 20 to prevent connection exhaustion.
Tested with 5K req/s. Do not modify without load testing.
---
Example 2: String Matching
<!-- DECISION-RATE-001 -->
## Decision: Rate Limit Constants
**Status**: Active
**Date**: 2024-02-10
**Severity**: Warning
**Rules**:
```json
{
"type": "file",
"pattern": "src/constants.ts",
"content_rules": [
{
"mode": "string",
"patterns": ["MAX_REQUESTS", "RATE_LIMIT"]
}
]
}
### Context
Rate limiting constants require security review.
Example 3: Regex Matching
<!-- DECISION-SEC-001 -->
## Decision: No Hardcoded Credentials
**Status**: Active
**Date**: 2024-03-01
**Severity**: Critical
**Rules**:
```json
{
"type": "file",
"pattern": "src/**/*.{ts,js}",
"exclude": "**/*.test.{ts,js}",
"content_rules": [
{
"mode": "regex",
"pattern": "(password|api[_-]?key|secret)\\s*[=:]\\s*['\"][^'\"]+['\"]",
"flags": "i"
}
]
}
### Context
Detects hardcoded credentials. Use environment variables
or AWS Secrets Manager.
Example 4: Complex Logic
<!-- DECISION-AUTH-001 -->
## Decision: Authentication Changes
**Status**: Active
**Date**: 2024-03-15
**Severity**: Critical
**Rules**:
```json
{
"match_mode": "all",
"conditions": [
{
"type": "file",
"pattern": "config/auth.yml"
},
{
"match_mode": "any",
"conditions": [
{ "type": "file", "pattern": "src/auth/**/*.ts" },
{ "type": "file", "pattern": "src/middleware/auth.ts" }
]
}
]
}
### Context
Auth config AND auth code must both change for this alert.
Example 5: Line Range
<!-- DECISION-LICENSE-001 -->
## Decision: License Header Protection
**Status**: Active
**Date**: 2024-01-01
**Severity**: Warning
**Rules**:
```json
{
"type": "file",
"pattern": "src/**/*.ts",
"content_rules": [
{
"mode": "line_range",
"start": 1,
"end": 10
}
]
}
### Context
Lines 1-10 contain license header. Changes require legal approval.
Validation
Common Errors
| Error | Cause | Fix |
|---|---|---|
Decision missing required fields | Missing ID or title | Add <!-- DECISION-ID --> and ## Decision: |
Failed to parse JSON rules | Invalid JSON | Validate with JSON linter |
Rule nesting exceeds max depth | >10 levels | Flatten structure |
Invalid regex pattern | Bad regex syntax | Test regex first |
Unsafe regex pattern detected | ReDoS risk | Simplify pattern |
Validation Warnings
Date warnings:
- Future date: "Date is in the future - is this correct?"
- Old date: "Date is >10 years old - consider archiving"
Format warnings:
- Invalid date format: "Use YYYY-MM-DD"
- Invalid ID: "Must start with DECISION-"
Best Practices
Writing Decisions
- Use descriptive IDs:
DECISION-DB-POOL-001>DECISION-001 - Write clear context: Explain why, not just what
- Start simple: File patterns before advanced rules
- Test regex: Use online testers before committing
- Keep nesting shallow: Avoid deep rule structures
- External files: For complex, reusable rules
- Regular review: Archive outdated decisions
Severity Guidelines
| Use Critical | Use Warning | Use Info |
|---|---|---|
| Production outage risk | Best practices | Documentation |
| Security vulnerabilities | Performance | Coding standards |
| Data loss possible | Breaking changes | Patterns |
| Compliance issues | Tech debt | Historical context |
Context Guidelines
Include:
- Why: Reason for decision
- Impact: What happens if ignored
- Tested: How it was validated
- Links: Slack, Jira, PRs
About
Decision Guardian is created and maintained by Ali Abbas as part of the Decispher project.
Made with ❤️ by Ali Abbas