Author: Ali Abbas
Project: Decispher
Complete reference for the decisions file format used by Decision Guardian.
- File Location
- Decision Structure
- Required Fields
- Optional Fields
- File Patterns
- Advanced Rules
- Content Matching
- Examples
- Validation
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-ID -->
## Decision: Title
**Status**: Active
**Date**: YYYY-MM-DD
**Severity**: Critical|Warning|Info
**Files**:
- `pattern`
### Context
Explanation of the decision.
---- Decision Marker:
<!-- DECISION-ID --> - Title:
## Decision: Title - At least one of:
FilesorRules
All other fields have defaults if omitted.
<!-- 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 -->## Decision: Your Decision TitleGuidelines:
- Clear and descriptive
- Explains what the decision covers
- Keep under 100 characters
**Status**: Active| Status | Synonyms | Behavior |
|---|---|---|
active |
enabled, live |
✅ Triggers PR alerts |
deprecated |
obsolete |
|
superseded |
replaced |
|
archived |
inactive |
Default: active
Important: Only active decisions trigger alerts.
**Date**: 2024-01-15Format: ISO 8601 (YYYY-MM-DD)
Validation:
- Future dates trigger warning
- Dates >10 years old suggest review
Default: Current date
**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
**Files**:
- `src/db/pool.ts`
- `src/**/*.ts`
- `config/*.yml`| 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 |
Both backtick formats supported:
**Files**:
- `src/app.ts` <!-- recommended -->
- src/utils.ts <!-- also works -->**Files**:
- `package.json` # Exact file
- `src/**/*.ts` # All TypeScript
- `config/*.{yml,yaml}` # Config files
- `migrations/**/*.sql` # SQL migrations
- `!migrations/**/test_*.sql` # Exclude testsJSON-based rules for fine-grained control.
**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"
}
]
}{
"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 |
{
"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 |
Rules for matching within file diffs.
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. |
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 |
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
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) |
Match any change to the file.
{
"mode": "full_file"
}Use case: Files always needing review
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 |
{
"match_mode": "any",
"conditions": [
{ "type": "file", "pattern": "Dockerfile" },
{ "type": "file", "pattern": "docker-compose.yml" }
]
}Triggers if either file changes.
{
"match_mode": "all",
"conditions": [
{ "type": "file", "pattern": "src/auth/**/*.ts" },
{ "type": "file", "pattern": "config/auth.yml" }
]
}Triggers only if both change.
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.
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
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)<!-- 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.
---<!-- 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.
<!-- 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.
<!-- 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.
<!-- 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.
| 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 |
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-"
- 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
| 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 |
Include:
- Why: Reason for decision
- Impact: What happens if ignored
- Tested: How it was validated
- Links: Slack, Jira, PRs
Decision Guardian is created and maintained by Ali Abbas as part of the Decispher project.
Made with ❤️ by Ali Abbas