decision-guardian 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +792 -0
- package/dist/adapters/github/actions-logger.js +88 -0
- package/dist/adapters/github/comment.js +601 -0
- package/dist/adapters/github/github-provider.js +260 -0
- package/dist/adapters/github/health.js +56 -0
- package/dist/adapters/local/console-logger.js +46 -0
- package/dist/adapters/local/local-git-provider.js +247 -0
- package/dist/cli/commands/check.js +134 -0
- package/dist/cli/commands/init.js +58 -0
- package/dist/cli/commands/template.js +70 -0
- package/dist/cli/formatter.js +68 -0
- package/dist/cli/index.js +12458 -0
- package/dist/cli/licenses.txt +143 -0
- package/dist/cli/paths.js +40 -0
- package/dist/core/content-matchers.js +333 -0
- package/dist/core/health.js +52 -0
- package/dist/core/interfaces/index.js +2 -0
- package/dist/core/interfaces/logger.js +2 -0
- package/dist/core/interfaces/scm-provider.js +5 -0
- package/dist/core/logger.js +20 -0
- package/dist/core/matcher.js +184 -0
- package/dist/core/metrics.js +87 -0
- package/dist/core/parser.js +338 -0
- package/dist/core/rule-evaluator.js +186 -0
- package/dist/core/rule-parser.js +211 -0
- package/dist/core/rule-types.js +22 -0
- package/dist/core/trie.js +83 -0
- package/dist/core/types.js +2 -0
- package/dist/index.js +61142 -0
- package/dist/licenses.txt +758 -0
- package/dist/main.js +290 -0
- package/dist/telemetry/payload.js +25 -0
- package/dist/telemetry/privacy.js +37 -0
- package/dist/telemetry/sender.js +40 -0
- package/dist/version.js +7 -0
- package/package.json +60 -0
- package/templates/advanced-rules.md +94 -0
- package/templates/api.md +70 -0
- package/templates/basic.md +38 -0
- package/templates/database.md +81 -0
- package/templates/security.md +89 -0
package/README.md
ADDED
|
@@ -0,0 +1,792 @@
|
|
|
1
|
+
# Decision Guardian
|
|
2
|
+
|
|
3
|
+
> **Prevent institutional amnesia by surfacing past architectural decisions directly on Pull Requests (or CLI checks).**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/marketplace/actions/decision-guardian)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://decispher.com)
|
|
8
|
+
[](https://decision-guardian.decispher.com/)
|
|
9
|
+
[](SECURITY.md)
|
|
10
|
+
|
|
11
|
+
Decision Guardian is a tool that automatically surfaces architectural decisions and critical context when code changes modify protected files. Use it as a **GitHub Action** for automated PR checks, or as a **CLI tool** for local development and any CI/CD system. Instead of relying on tribal knowledge, Decision Guardian proactively alerts teams when changes touch sensitive code.
|
|
12
|
+
|
|
13
|
+
**Created by [Ali Abbas](https://github.com/gr8-alizaidi) • Part of the [Decispher](https://decispher.com) project**
|
|
14
|
+
|
|
15
|
+
<div align="center">
|
|
16
|
+
<img src="docs/common/images/demo.gif" alt="Decision Guardian Demo" width="100%">
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 🎯 The Problem
|
|
22
|
+
|
|
23
|
+
Engineering teams lose critical context when:
|
|
24
|
+
- Senior engineers leave
|
|
25
|
+
- Architectural decisions aren't documented
|
|
26
|
+
- New developers modify sensitive code without understanding why
|
|
27
|
+
|
|
28
|
+
**Real scenario:**
|
|
29
|
+
```
|
|
30
|
+
March 2023: Team chooses Postgres over MongoDB for ACID compliance
|
|
31
|
+
September 2023: Senior engineer who made decision leaves
|
|
32
|
+
March 2024: New developer opens PR to switch to MongoDB
|
|
33
|
+
Result: Team wastes 3 months re-evaluating the same decision
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Decision Guardian prevents this by making past decisions visible when they matter most.**
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 🛡️ Trust & Safety
|
|
41
|
+
|
|
42
|
+
> **"Is this safe to run on my private repo?"**
|
|
43
|
+
|
|
44
|
+
We explicitly guarantee:
|
|
45
|
+
|
|
46
|
+
- ✅ **No source code leaves your repo**: Only anonymous aggregate counts (file count, match count, duration) are collected — never file contents, paths, or identifiers.
|
|
47
|
+
- ✅ **Opt-out telemetry**: Anonymous usage metrics are sent to Cloudflare to help improve the tool. Disable with `DG_TELEMETRY=0`. See [PRIVACY.md](PRIVACY.md).
|
|
48
|
+
- ✅ **Read-only access**: We only require write permissions to post comments on Pull Requests.
|
|
49
|
+
|
|
50
|
+
> **Note**: v1.1 introduces opt-out telemetry. If your organization requires zero external network calls, set `DG_TELEMETRY=0` in your workflow `env`.
|
|
51
|
+
|
|
52
|
+
See [SECURITY.md](SECURITY.md) for our full security policy.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 🚀 Quick Start
|
|
57
|
+
|
|
58
|
+
### GitHub Action Setup
|
|
59
|
+
|
|
60
|
+
### 1. Create Decision File
|
|
61
|
+
|
|
62
|
+
Create `.decispher/decisions.md`:
|
|
63
|
+
|
|
64
|
+
```markdown
|
|
65
|
+
<!-- DECISION-DB-001 -->
|
|
66
|
+
## Decision: Database Choice for Billing
|
|
67
|
+
|
|
68
|
+
**Status**: Active
|
|
69
|
+
**Date**: 2024-03-15
|
|
70
|
+
**Severity**: Critical
|
|
71
|
+
|
|
72
|
+
**Files**:
|
|
73
|
+
- `src/db/pool.ts`
|
|
74
|
+
- `config/database.{yml,yaml}`
|
|
75
|
+
|
|
76
|
+
### Context
|
|
77
|
+
|
|
78
|
+
We chose Postgres over MongoDB because billing requires ACID compliance.
|
|
79
|
+
MongoDB doesn't guarantee consistency for financial transactions.
|
|
80
|
+
|
|
81
|
+
**Alternatives rejected:**
|
|
82
|
+
- MongoDB: No ACID guarantees
|
|
83
|
+
- Redis: Added unnecessary complexity
|
|
84
|
+
|
|
85
|
+
**Related:**
|
|
86
|
+
- [Slack thread](link)
|
|
87
|
+
- [Architecture review](link)
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 2. Add Workflow
|
|
93
|
+
|
|
94
|
+
Create `.github/workflows/decision-guardian.yml`:
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
name: Decision Guardian
|
|
98
|
+
|
|
99
|
+
on:
|
|
100
|
+
pull_request:
|
|
101
|
+
|
|
102
|
+
permissions:
|
|
103
|
+
pull-requests: write
|
|
104
|
+
contents: read
|
|
105
|
+
|
|
106
|
+
jobs:
|
|
107
|
+
check:
|
|
108
|
+
runs-on: ubuntu-latest
|
|
109
|
+
steps:
|
|
110
|
+
- uses: actions/checkout@v4
|
|
111
|
+
|
|
112
|
+
- uses: DecispherHQ/decision-guardian@v1
|
|
113
|
+
with:
|
|
114
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
115
|
+
decision_file: '.decispher/decisions.md'
|
|
116
|
+
fail_on_critical: true
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> **Production tip**: Add a `concurrency` block to prevent duplicate comments from parallel runs. See the [full workflow example](docs/github/APP_WORKING.md) for a production-ready configuration.
|
|
120
|
+
|
|
121
|
+
### 3. See It Work
|
|
122
|
+
|
|
123
|
+
When someone opens a PR modifying `src/db/pool.ts`, Decision Guardian automatically comments with the context from `DECISION-DB-001`.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### CLI Setup
|
|
128
|
+
|
|
129
|
+
For local development or non-GitHub CI systems:
|
|
130
|
+
|
|
131
|
+
#### 1. Install
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npm install -g decision-guardian
|
|
135
|
+
# or use directly without installation
|
|
136
|
+
npx decision-guardian --help
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### 2. Check Changes Locally
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
# Check staged changes
|
|
143
|
+
decision-guardian check .decispher/decisions.md
|
|
144
|
+
|
|
145
|
+
# Check against a branch
|
|
146
|
+
decision-guardian check .decispher/decisions.md --branch main
|
|
147
|
+
|
|
148
|
+
# Check all uncommitted changes
|
|
149
|
+
decision-guardian check .decispher/decisions.md --all
|
|
150
|
+
|
|
151
|
+
# Auto-discover all decision files
|
|
152
|
+
decision-guardian checkall --fail-on-critical
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
#### 3. Use in Any CI System
|
|
156
|
+
|
|
157
|
+
**GitLab CI:**
|
|
158
|
+
```yaml
|
|
159
|
+
check-decisions:
|
|
160
|
+
script:
|
|
161
|
+
- npx decision-guardian check .decispher/decisions.md --branch $CI_MERGE_REQUEST_TARGET_BRANCH_NAME --fail-on-critical
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Jenkins:**
|
|
165
|
+
```groovy
|
|
166
|
+
stage('Check Decisions') {
|
|
167
|
+
steps {
|
|
168
|
+
sh 'npx decision-guardian checkall --fail-on-critical'
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Pre-commit Hook:**
|
|
174
|
+
```bash
|
|
175
|
+
#!/bin/sh
|
|
176
|
+
# Single file check
|
|
177
|
+
npx decision-guardian check .decispher/decisions.md --staged --fail-on-critical
|
|
178
|
+
|
|
179
|
+
# Or use checkall to auto-discover all decision files (recommended for multi-file setups)
|
|
180
|
+
npx decision-guardian checkall --fail-on-critical
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## ✨ Features
|
|
186
|
+
|
|
187
|
+
### Core Capabilities
|
|
188
|
+
|
|
189
|
+
✅ **Automatic Context Surfacing**
|
|
190
|
+
- Posts PR comments when protected files change
|
|
191
|
+
- Groups by severity (Critical, Warning, Info)
|
|
192
|
+
- Shows decision rationale and links
|
|
193
|
+
|
|
194
|
+
✅ **Flexible Matching**
|
|
195
|
+
- File patterns with glob support (`src/**/*.ts`, `!**/*.test.ts`)
|
|
196
|
+
- Advanced rules (regex, content matching, boolean logic)
|
|
197
|
+
- Directory scanning (multiple decision files)
|
|
198
|
+
|
|
199
|
+
✅ **Production-Ready**
|
|
200
|
+
- Handles PRs with 3,000+ files
|
|
201
|
+
- Idempotent comments (no spam)
|
|
202
|
+
- Rate limit handling with retry
|
|
203
|
+
- ReDoS protection for regex
|
|
204
|
+
|
|
205
|
+
✅ **Smart Behavior**
|
|
206
|
+
- Updates existing comments instead of creating duplicates
|
|
207
|
+
- Only active decisions trigger alerts
|
|
208
|
+
- Self-healing duplicate cleanup
|
|
209
|
+
- Auto-resolves to 'All Clear' when issues are fixed
|
|
210
|
+
- Progressive truncation for large PRs
|
|
211
|
+
|
|
212
|
+
✅ **Local CLI** ([docs](docs/cli/CLI.md))
|
|
213
|
+
- Run `check` or `checkall` commands locally
|
|
214
|
+
- Compare against staged changes, branches, or all uncommitted files
|
|
215
|
+
- Works with any CI system (GitLab, Jenkins, CircleCI, etc.)
|
|
216
|
+
- Initialize projects with templates (`init` command)
|
|
217
|
+
- Single-file bundle (~430KB)
|
|
218
|
+
|
|
219
|
+
✅ **Opt-in Telemetry** ([docs](docs/common/TELEMETRY.md))
|
|
220
|
+
- Privacy-first: no source code, no identifiers
|
|
221
|
+
- Blocklist-enforced payload validation
|
|
222
|
+
- Fire-and-forget, never blocks the tool
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Configuration
|
|
227
|
+
|
|
228
|
+
### Inputs
|
|
229
|
+
|
|
230
|
+
```yaml
|
|
231
|
+
- uses: DecispherHQ/decision-guardian@v1
|
|
232
|
+
with:
|
|
233
|
+
decision_file: '.decispher/decisions.md' # or directory
|
|
234
|
+
fail_on_critical: false # block PRs?
|
|
235
|
+
fail_on_error: false # strict mode
|
|
236
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
| Input | Default | Description |
|
|
240
|
+
|-------|---------|-------------|
|
|
241
|
+
| `decision_file` | `.decispher/decisions.md` | Path to file or directory |
|
|
242
|
+
| `fail_on_critical` | `false` | Fail PR check on critical violations |
|
|
243
|
+
| `fail_on_error` | `false` | Fail on parse errors |
|
|
244
|
+
| `token` | `${{ github.token }}` | GitHub token (required) |
|
|
245
|
+
|
|
246
|
+
> **Note**: Telemetry is controlled via the `DG_TELEMETRY` environment variable. Set `DG_TELEMETRY=0` to disable. See [Privacy Policy](PRIVACY.md) for details.
|
|
247
|
+
|
|
248
|
+
### Outputs
|
|
249
|
+
|
|
250
|
+
```yaml
|
|
251
|
+
- uses: DecispherHQ/decision-guardian@v1
|
|
252
|
+
id: check
|
|
253
|
+
|
|
254
|
+
- run: echo "Matches: ${{ steps.check.outputs.matches_found }}"
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
| Output | Description |
|
|
258
|
+
|--------|-------------|
|
|
259
|
+
| `matches_found` | Number of decisions matched |
|
|
260
|
+
| `critical_count` | Critical severity violations |
|
|
261
|
+
| `metrics` | Performance data (JSON) |
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Decision File Format
|
|
266
|
+
|
|
267
|
+
### Basic Structure
|
|
268
|
+
|
|
269
|
+
```markdown
|
|
270
|
+
<!-- DECISION-ID -->
|
|
271
|
+
## Decision: Title
|
|
272
|
+
|
|
273
|
+
**Status**: Active
|
|
274
|
+
**Date**: YYYY-MM-DD
|
|
275
|
+
**Severity**: Critical|Warning|Info
|
|
276
|
+
|
|
277
|
+
**Files**:
|
|
278
|
+
- `pattern`
|
|
279
|
+
|
|
280
|
+
### Context
|
|
281
|
+
|
|
282
|
+
Explanation of the decision.
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Field Reference
|
|
288
|
+
|
|
289
|
+
**Decision ID**: `DECISION-[CATEGORY-]NUMBER`
|
|
290
|
+
- Examples: `DECISION-001`, `DECISION-DB-001`, `DECISION-API-AUTH-001`
|
|
291
|
+
- Must be uppercase, can include hyphens
|
|
292
|
+
|
|
293
|
+
**Status** (only `Active` triggers alerts):
|
|
294
|
+
- `Active` - Currently enforced
|
|
295
|
+
- `Deprecated` - Being phased out
|
|
296
|
+
- `Superseded` - Replaced by newer decision
|
|
297
|
+
- `Archived` - Historical reference only
|
|
298
|
+
|
|
299
|
+
**Severity**:
|
|
300
|
+
- `Critical` - Blocks PR if `fail_on_critical: true`
|
|
301
|
+
- `Warning` - Important but non-blocking
|
|
302
|
+
- `Info` - FYI only
|
|
303
|
+
|
|
304
|
+
**Files** (glob patterns):
|
|
305
|
+
- `src/db/pool.ts` - Exact file
|
|
306
|
+
- `src/**/*.ts` - All .ts files (recursive)
|
|
307
|
+
- `config/*.yml` - .yml in config/ only
|
|
308
|
+
- `!**/*.test.ts` - Exclude tests
|
|
309
|
+
|
|
310
|
+
**Context**: Explain why the decision was made, alternatives rejected, and links to related docs.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Advanced Rules
|
|
315
|
+
|
|
316
|
+
For complex scenarios, use JSON-based rules:
|
|
317
|
+
|
|
318
|
+
```markdown
|
|
319
|
+
**Rules**:
|
|
320
|
+
```json
|
|
321
|
+
{
|
|
322
|
+
"type": "file",
|
|
323
|
+
"pattern": "src/**/*.ts",
|
|
324
|
+
"exclude": "**/*.test.ts",
|
|
325
|
+
"content_rules": [
|
|
326
|
+
{
|
|
327
|
+
"mode": "regex",
|
|
328
|
+
"pattern": "password\\s*=\\s*['\"]",
|
|
329
|
+
"flags": "i"
|
|
330
|
+
}
|
|
331
|
+
]
|
|
332
|
+
}
|
|
333
|
+
```
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Rule Types
|
|
337
|
+
|
|
338
|
+
**File Rules**:
|
|
339
|
+
```json
|
|
340
|
+
{
|
|
341
|
+
"type": "file",
|
|
342
|
+
"pattern": "src/api/**/*.ts",
|
|
343
|
+
"exclude": "**/*.test.ts",
|
|
344
|
+
"content_rules": [...]
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**Content Modes**:
|
|
349
|
+
- `string` - Match literal strings
|
|
350
|
+
- `regex` - Pattern matching (5s timeout, ReDoS-protected)
|
|
351
|
+
- `line_range` - Specific line numbers
|
|
352
|
+
- `full_file` - Any change
|
|
353
|
+
- `json_path` - Target JSON keys (hierarchical match)
|
|
354
|
+
|
|
355
|
+
**Boolean Logic**:
|
|
356
|
+
```json
|
|
357
|
+
{
|
|
358
|
+
"match_mode": "any", // OR
|
|
359
|
+
"conditions": [...]
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
```json
|
|
364
|
+
{
|
|
365
|
+
"match_mode": "all", // AND
|
|
366
|
+
"conditions": [...]
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Nesting**: Up to 10 levels deep
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## Examples
|
|
375
|
+
|
|
376
|
+
### Example 1: Database Configuration
|
|
377
|
+
|
|
378
|
+
```markdown
|
|
379
|
+
<!-- DECISION-DB-001 -->
|
|
380
|
+
## Decision: Connection Pool Size
|
|
381
|
+
|
|
382
|
+
**Status**: Active
|
|
383
|
+
**Date**: 2024-01-15
|
|
384
|
+
**Severity**: Critical
|
|
385
|
+
|
|
386
|
+
**Files**:
|
|
387
|
+
- `src/db/pool.ts`
|
|
388
|
+
- `config/database.yml`
|
|
389
|
+
|
|
390
|
+
### Context
|
|
391
|
+
|
|
392
|
+
Pool size fixed at 20 connections to prevent exhaustion.
|
|
393
|
+
|
|
394
|
+
Tested with production load (5K req/s). Higher values caused
|
|
395
|
+
connection leaks under sustained traffic.
|
|
396
|
+
|
|
397
|
+
**Do not modify without load testing.**
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Example 2: Security Pattern
|
|
403
|
+
|
|
404
|
+
```markdown
|
|
405
|
+
<!-- DECISION-SEC-001 -->
|
|
406
|
+
## Decision: No Hardcoded Credentials
|
|
407
|
+
|
|
408
|
+
**Status**: Active
|
|
409
|
+
**Date**: 2024-02-01
|
|
410
|
+
**Severity**: Critical
|
|
411
|
+
|
|
412
|
+
**Rules**:
|
|
413
|
+
```json
|
|
414
|
+
{
|
|
415
|
+
"type": "file",
|
|
416
|
+
"pattern": "src/**/*.{ts,js}",
|
|
417
|
+
"exclude": "**/*.test.{ts,js}",
|
|
418
|
+
"content_rules": [
|
|
419
|
+
{
|
|
420
|
+
"mode": "regex",
|
|
421
|
+
"pattern": "(password|api[_-]?key|secret)\\s*[=:]\\s*['\"][^'\"]+['\"]",
|
|
422
|
+
"flags": "i"
|
|
423
|
+
}
|
|
424
|
+
]
|
|
425
|
+
}
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
### Context
|
|
429
|
+
|
|
430
|
+
Detects hardcoded credentials. Use environment variables
|
|
431
|
+
or AWS Secrets Manager instead.
|
|
432
|
+
|
|
433
|
+
---
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Example 3: API Changes
|
|
437
|
+
|
|
438
|
+
```markdown
|
|
439
|
+
<!-- DECISION-API-001 -->
|
|
440
|
+
## Decision: Public API v1 Protection
|
|
441
|
+
|
|
442
|
+
**Status**: Active
|
|
443
|
+
**Date**: 2024-03-01
|
|
444
|
+
**Severity**: Warning
|
|
445
|
+
|
|
446
|
+
**Files**:
|
|
447
|
+
- `src/api/v1/**/*.ts`
|
|
448
|
+
- `openapi.yaml`
|
|
449
|
+
|
|
450
|
+
### Context
|
|
451
|
+
|
|
452
|
+
v1 API changes affect external clients.
|
|
453
|
+
|
|
454
|
+
**Before merging:**
|
|
455
|
+
- Update API docs
|
|
456
|
+
- Notify integration partners
|
|
457
|
+
- Version bump if breaking
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## 🏗️ Architecture
|
|
465
|
+
|
|
466
|
+
### Core Components
|
|
467
|
+
|
|
468
|
+
```
|
|
469
|
+
┌────────────────────────────────────────────────────┐
|
|
470
|
+
│ DECISION GUARDIAN │
|
|
471
|
+
├────────────────────────────────────────────────────┤
|
|
472
|
+
│ │
|
|
473
|
+
│ ┌──────────────────────────────────────────────┐ │
|
|
474
|
+
│ │ DECISION PARSER (AST-based) │ │
|
|
475
|
+
│ │ - Markdown parsing with remark │ │
|
|
476
|
+
│ │ - JSON rule extraction & validation │ │
|
|
477
|
+
│ │ - Multi-file directory support │ │
|
|
478
|
+
│ └──────────────────────────────────────────────┘ │
|
|
479
|
+
│ ↓ │
|
|
480
|
+
│ ┌──────────────────────────────────────────────┐ │
|
|
481
|
+
│ │ DECISION INDEX (Prefix Trie) │ │
|
|
482
|
+
│ │ - O(log n) file lookup │ │
|
|
483
|
+
│ │ - Wildcard pattern optimization │ │
|
|
484
|
+
│ └──────────────────────────────────────────────┘ │
|
|
485
|
+
│ ↓ │
|
|
486
|
+
│ ┌──────────────────────────────────────────────┐ │
|
|
487
|
+
│ │ FILE MATCHER (Rule Evaluator) │ │
|
|
488
|
+
│ │ - Glob pattern matching (minimatch) │ │
|
|
489
|
+
│ │ - Advanced rule evaluation │ │
|
|
490
|
+
│ │ - Content diff analysis │ │
|
|
491
|
+
│ │ - Parallel processing │ │
|
|
492
|
+
│ └──────────────────────────────────────────────┘ │
|
|
493
|
+
│ ↓ │
|
|
494
|
+
│ ┌──────────────────────────────────────────────┐ │
|
|
495
|
+
│ │ COMMENT MANAGER (Idempotent) │ │
|
|
496
|
+
│ │ - Hash-based update detection │ │
|
|
497
|
+
│ │ - Self-healing duplicate cleanup │ │
|
|
498
|
+
│ │ - Progressive truncation (6 layers) │ │
|
|
499
|
+
│ │ - Retry with exponential backoff │ │
|
|
500
|
+
│ └──────────────────────────────────────────────┘ │
|
|
501
|
+
│ │
|
|
502
|
+
└────────────────────────────────────────────────────┘
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
**High-level flow:**
|
|
507
|
+
|
|
508
|
+
```
|
|
509
|
+
PR Created → Parse Decisions → Match Files → Post Comment → Check Status
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
**Key components:**
|
|
513
|
+
- **Parser** (`src/core/parser.ts`): Markdown → structured data
|
|
514
|
+
- **Matcher** (`src/core/matcher.ts`): Trie-based file matching
|
|
515
|
+
- **Rule Evaluator** (`src/core/rule-evaluator.ts`): Advanced rules
|
|
516
|
+
- **Comment Manager** (`src/adapters/github/comment.ts`): Idempotent PR comments
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
### Key Optimizations
|
|
520
|
+
|
|
521
|
+
- **Prefix Trie**: Avoids O(N×M) file-decision comparisons
|
|
522
|
+
- **Streaming Mode**: Processes PRs with 3000+ files without OOM
|
|
523
|
+
- **Smart Caching**: Regex results cached to prevent ReDoS
|
|
524
|
+
- **Batch Processing**: Parallel evaluation with concurrency limits
|
|
525
|
+
- **Progressive Truncation**: 6-layer fallback ensures comments always fit
|
|
526
|
+
|
|
527
|
+
### Security
|
|
528
|
+
|
|
529
|
+
- ✅ Path traversal protection
|
|
530
|
+
- ✅ ReDoS prevention (VM sandbox with timeout)
|
|
531
|
+
- ✅ Input validation (Zod schemas)
|
|
532
|
+
- ✅ Safe regex checking (safe-regex)
|
|
533
|
+
- ✅ Content size limits
|
|
534
|
+
- ✅ Depth limits on nested rules
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## 📊 Performance
|
|
539
|
+
|
|
540
|
+
**Benchmark Results** (MacBook Pro M1, 16GB RAM):
|
|
541
|
+
|
|
542
|
+
| Scenario | Files | Decisions | Time | Memory |
|
|
543
|
+
|----------|-------|-----------|------|--------|
|
|
544
|
+
| Small PR | 10 | 50 | 1.2s | 45MB |
|
|
545
|
+
| Medium PR | 100 | 200 | 2.8s | 78MB |
|
|
546
|
+
| Large PR | 500 | 500 | 8.4s | 142MB |
|
|
547
|
+
| Huge PR | 3000 | 1000 | 34s | 289MB |
|
|
548
|
+
|
|
549
|
+
**API Calls**: ~2-4 per run (list files, create/update comment)
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
---
|
|
553
|
+
|
|
554
|
+
## Best Practices
|
|
555
|
+
|
|
556
|
+
### Writing Decisions
|
|
557
|
+
|
|
558
|
+
**Be specific:**
|
|
559
|
+
```markdown
|
|
560
|
+
❌ This is important. Be careful.
|
|
561
|
+
|
|
562
|
+
✅ Rate limiting config. Changes can:
|
|
563
|
+
- Block legitimate users (too strict)
|
|
564
|
+
- Allow abuse (too loose)
|
|
565
|
+
- Cause OOM (incorrect values)
|
|
566
|
+
|
|
567
|
+
Before merging: Load test with 2x traffic
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
**Include context:**
|
|
571
|
+
```markdown
|
|
572
|
+
### Context
|
|
573
|
+
|
|
574
|
+
**Why**: Billing requires ACID compliance
|
|
575
|
+
**Impact**: Data loss risk if violated
|
|
576
|
+
**Tested**: Load tested at 10K req/s
|
|
577
|
+
**Links**: [Slack](url), [Jira](url)
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
**Use appropriate severity:**
|
|
581
|
+
- `Critical`: Production impact, security, data loss
|
|
582
|
+
- `Warning`: Best practices, performance
|
|
583
|
+
- `Info`: Documentation, patterns
|
|
584
|
+
|
|
585
|
+
### Team Workflow
|
|
586
|
+
|
|
587
|
+
**Small teams (<10):**
|
|
588
|
+
- Single file: `.decispher/decisions.md`
|
|
589
|
+
- Start with `Info/Warning` severity
|
|
590
|
+
- Gradually add `Critical` as patterns emerge
|
|
591
|
+
|
|
592
|
+
**Medium teams (10-50):**
|
|
593
|
+
- Directory structure:
|
|
594
|
+
```
|
|
595
|
+
.decispher/
|
|
596
|
+
├── backend/
|
|
597
|
+
├── frontend/
|
|
598
|
+
└── infrastructure/
|
|
599
|
+
```
|
|
600
|
+
- Use `fail_on_critical: true`
|
|
601
|
+
- Quarterly decision reviews
|
|
602
|
+
|
|
603
|
+
**Large teams (50+):**
|
|
604
|
+
- Federated ownership
|
|
605
|
+
- CODEOWNERS on `.decispher/`
|
|
606
|
+
- Decision review board
|
|
607
|
+
- Metrics tracking
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## Troubleshooting
|
|
612
|
+
|
|
613
|
+
### Common Issues
|
|
614
|
+
|
|
615
|
+
**"Not a pull request event"**
|
|
616
|
+
```yaml
|
|
617
|
+
on:
|
|
618
|
+
pull_request: # ✅ Correct
|
|
619
|
+
# Not: push, schedule
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**"Failed to read file: ENOENT"**
|
|
623
|
+
- Verify file exists: `ls .decispher/decisions.md`
|
|
624
|
+
- Check path in workflow matches actual location
|
|
625
|
+
- Ensure file is committed
|
|
626
|
+
|
|
627
|
+
**"Path traversal detected"**
|
|
628
|
+
```yaml
|
|
629
|
+
decision_file: '.decispher/decisions.md' # ✅
|
|
630
|
+
decision_file: '../decisions.md' # ❌
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
**No comment posted**
|
|
634
|
+
- Check permissions: `pull-requests: write`
|
|
635
|
+
- Verify status is `Active` (not `Archived`)
|
|
636
|
+
- Check files match patterns
|
|
637
|
+
|
|
638
|
+
### Debug Mode
|
|
639
|
+
|
|
640
|
+
```yaml
|
|
641
|
+
env:
|
|
642
|
+
ACTIONS_STEP_DEBUG: true
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
## 🛠️ Development
|
|
649
|
+
|
|
650
|
+
### Setup
|
|
651
|
+
|
|
652
|
+
```bash
|
|
653
|
+
git clone https://github.com/DecispherHQ/decision-guardian.git
|
|
654
|
+
cd decision-guardian
|
|
655
|
+
npm install
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
### Build
|
|
659
|
+
|
|
660
|
+
```bash
|
|
661
|
+
npm run build # Compile TypeScript
|
|
662
|
+
npm run bundle # Bundle Action for distribution
|
|
663
|
+
npm run build:cli # Bundle CLI (~430KB)
|
|
664
|
+
npm test # Run tests (109 tests)
|
|
665
|
+
npm run lint # Check code quality
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
### CLI Development
|
|
669
|
+
|
|
670
|
+
```bash
|
|
671
|
+
# Run CLI from source
|
|
672
|
+
npx ts-node src/cli/index.ts check .decispher/decisions.md
|
|
673
|
+
|
|
674
|
+
# Build and test CLI bundle
|
|
675
|
+
npm run build:cli
|
|
676
|
+
node dist/cli/index.js --help
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
### Documentation
|
|
680
|
+
|
|
681
|
+
- [CLI Usage](docs/cli/CLI.md)
|
|
682
|
+
- [Architecture](docs/common/ARCHITECTURE.md)
|
|
683
|
+
- [Templates](docs/common/TEMPLATES.md)
|
|
684
|
+
- [Telemetry](docs/common/TELEMETRY.md)
|
|
685
|
+
|
|
686
|
+
|
|
687
|
+
---
|
|
688
|
+
|
|
689
|
+
## 🤝 Contributing
|
|
690
|
+
|
|
691
|
+
We welcome contributions! Decision Guardian is open source (MIT) and maintained by [Decispher](https://decispher.com).
|
|
692
|
+
|
|
693
|
+
### Ways to Contribute
|
|
694
|
+
|
|
695
|
+
1. **Report Bugs**: [Open an issue](https://github.com/DecispherHQ/decision-guardian/issues)
|
|
696
|
+
2. **Suggest Features**: [Start a discussion](https://github.com/DecispherHQ/decision-guardian/discussions)
|
|
697
|
+
3. **Submit PRs**: See [Contributing.md](Contributing.md)
|
|
698
|
+
4. **Improve Docs**: Fix typos, add examples
|
|
699
|
+
5. **Share**: Star ⭐ the repo, write blog posts
|
|
700
|
+
|
|
701
|
+
### Development Workflow
|
|
702
|
+
|
|
703
|
+
1. Fork the repository
|
|
704
|
+
2. Create a feature branch (`git checkout -b feature/amazing`)
|
|
705
|
+
3. Make your changes
|
|
706
|
+
4. Add tests (if applicable)
|
|
707
|
+
5. Run `npm test` and `npm run lint`
|
|
708
|
+
6. Commit with conventional commits (`feat:`, `fix:`, `docs:`)
|
|
709
|
+
7. Push and open a Pull Request
|
|
710
|
+
|
|
711
|
+
---
|
|
712
|
+
|
|
713
|
+
## 📝 FAQ
|
|
714
|
+
|
|
715
|
+
**Q: Can it prevent merges?**
|
|
716
|
+
A: Yes, when `fail_on_critical: true`.
|
|
717
|
+
|
|
718
|
+
**Q: Works with monorepos?**
|
|
719
|
+
A: Yes. Use path-specific patterns.
|
|
720
|
+
|
|
721
|
+
**Q: Works with private repos?**
|
|
722
|
+
A: Yes. Uses `GITHUB_TOKEN`.
|
|
723
|
+
|
|
724
|
+
**Q: Difference vs CODEOWNERS?**
|
|
725
|
+
A: CODEOWNERS assigns reviewers. Decision Guardian explains why review matters.
|
|
726
|
+
|
|
727
|
+
**Q: How do I skip for specific PRs?**
|
|
728
|
+
A: Use label condition:
|
|
729
|
+
```yaml
|
|
730
|
+
if: "!contains(github.event.pull_request.labels.*.name, 'skip-decisions')"
|
|
731
|
+
```
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
735
|
+
## 💬 Support
|
|
736
|
+
|
|
737
|
+
- **Website**: [decision-guardian.decispher.com](https://decision-guardian.decispher.com/)
|
|
738
|
+
- **Community**: [GitHub Discussions](https://github.com/DecispherHQ/decision-guardian/discussions)
|
|
739
|
+
- **Issues**: [Bug Reports](https://github.com/DecispherHQ/decision-guardian/issues)
|
|
740
|
+
- **Enterprise**: [Decispher Support](https://decision-guardian.decispher.com/support)
|
|
741
|
+
- **Email**: [decispher@gmail.com](mailto:decispher@gmail.com)
|
|
742
|
+
|
|
743
|
+
---
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
## 📄 License
|
|
747
|
+
|
|
748
|
+
**MIT License** - See [LICENSE](LICENSE) file for details.
|
|
749
|
+
|
|
750
|
+
Decision Guardian is free and open source.
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
## About
|
|
755
|
+
|
|
756
|
+
**Decision Guardian** is created and maintained by **Ali Abbas** as part of the **Decispher** project.
|
|
757
|
+
|
|
758
|
+
Decispher helps engineering teams preserve and leverage institutional knowledge.
|
|
759
|
+
|
|
760
|
+
**Connect:**
|
|
761
|
+
- GitHub: [@gr8-alizaidi](https://github.com/gr8-alizaidi)
|
|
762
|
+
- Twitter: [@gr8_alizaidi](https://twitter.com/gr8_alizaidi)
|
|
763
|
+
|
|
764
|
+
---
|
|
765
|
+
|
|
766
|
+
## 🙏 Acknowledgments
|
|
767
|
+
|
|
768
|
+
Built with:
|
|
769
|
+
- [minimatch](https://github.com/isaacs/minimatch) - Glob matching
|
|
770
|
+
- [parse-diff](https://github.com/sergeyt/parse-diff) - Unified diff parsing
|
|
771
|
+
- [zod](https://github.com/colinhacks/zod) - Runtime validation
|
|
772
|
+
- [safe-regex](https://github.com/substack/safe-regex) - ReDoS prevention
|
|
773
|
+
- [@actions/github](https://github.com/actions/toolkit) - GitHub API client
|
|
774
|
+
|
|
775
|
+
Inspired by:
|
|
776
|
+
- [Architecture Decision Records (ADR)](https://adr.github.io/)
|
|
777
|
+
- [CODEOWNERS](https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners)
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
---
|
|
781
|
+
|
|
782
|
+
## 🌟 Show Your Support
|
|
783
|
+
|
|
784
|
+
If Decision Guardian helps your team, please:
|
|
785
|
+
- ⭐ Star this repository
|
|
786
|
+
- 🐦 Tweet about it ([@decispher](https://twitter.com/decispher))
|
|
787
|
+
- 📝 Write a blog post
|
|
788
|
+
- 💼 Recommend it to colleagues
|
|
789
|
+
|
|
790
|
+
**Made with ❤️ by [Decispher](https://decispher.com)**
|
|
791
|
+
|
|
792
|
+
*Preventing institutional amnesia, one PR at a time.*
|