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

  1. File Location
  2. Decision Structure
  3. Required Fields
  4. Optional Fields
  5. File Patterns
  6. Advanced Rules
  7. Content Matching
  8. Examples
  9. Validation

File Location

Default: .decispher/decisions.md

Customizable via:

  • GitHub Action: decision_file input
  • CLI: positional argument to check <path>, or auto-discovered by checkall

Accepted values:

  • Single file: .decispher/decisions.md
  • Directory: .decispher/ (auto-discovers all .md files recursively)

Directory scanning:

  • Recursively finds all .md files
  • 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

  1. Decision Marker: <!-- DECISION-ID -->
  2. Title: ## Decision: Title
  3. At least one of: Files or Rules

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
StatusSynonymsBehavior
activeenabled, live✅ Triggers PR alerts
deprecatedobsolete⚠️ Parsed but ignored
supersededreplaced⚠️ Parsed but ignored
archivedinactive⚠️ 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
SeveritySynonymsDisplayFails check?
infoinformational, lowℹ️No
warningwarn, medium🟡No
criticalerror, 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

PatternMatchesExample
file.tsExact filesrc/app.ts
*.tsAll .ts in directorysrc/*.ts
**/*.tsAll .ts recursivelysrc/**/*.ts
{a,b}a or b*.{ts,js}
[a-z]Character rangetest[0-9].ts
!patternExclusion!**/*.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
}
PropertyTypeDefaultDescription
match_mode"any" | "all""any"Boolean logic for conditions
conditionsArrayRequiredList 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
}
PropertyTypeRequiredDescription
type"file"YesMust be "file"
patternStringYesGlob pattern for file matching
excludeStringNoGlob pattern to exclude files
content_match_mode"any" | "all"NoHow content_rules are combined. "any" (default) fires if any rule matches; "all" fires only when every rule matches
content_rulesArrayNoRules 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:

PropertyTypeDefaultDescription
match_changed_lines_onlyBooleanfalseBy 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_linesBooleanfalseBy 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

PropertyTypeDescription
mode"string"Required
patternsString[]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)

PropertyTypeDescription
mode"regex"Required
patternStringRegular expression pattern
flagsStringOptional 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

PropertyTypeDescription
mode"line_range"Required
startNumberStart line number (inclusive)
endNumberEnd 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

PropertyTypeDescription
mode"json_path"Required
pathsString[]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

ErrorCauseFix
Decision missing required fieldsMissing ID or titleAdd <!-- DECISION-ID --> and ## Decision:
Failed to parse JSON rulesInvalid JSONValidate with JSON linter
Rule nesting exceeds max depth>10 levelsFlatten structure
Invalid regex patternBad regex syntaxTest regex first
Unsafe regex pattern detectedReDoS riskSimplify 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

  1. Use descriptive IDs: DECISION-DB-POOL-001 > DECISION-001
  2. Write clear context: Explain why, not just what
  3. Start simple: File patterns before advanced rules
  4. Test regex: Use online testers before committing
  5. Keep nesting shallow: Avoid deep rule structures
  6. External files: For complex, reusable rules
  7. Regular review: Archive outdated decisions

Severity Guidelines

Use CriticalUse WarningUse Info
Production outage riskBest practicesDocumentation
Security vulnerabilitiesPerformanceCoding standards
Data loss possibleBreaking changesPatterns
Compliance issuesTech debtHistorical 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