Skip to content
Back to skills

Decision Journal

ASecurity

Extracts and structures development decisions from diffs, manages decision journal entries, and detects human gate triggers. Use when logging decisions during gh-start, gh-commit, or gh-address. Use when summarizing decisions for PR bodies or when checking for gate-triggering changes like new dependencies, security modifications, or scope deviations.

  • 7 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
data-aigobashgitapidatabasesecurity

Works with

  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add synaptiai/synapti-marketplace --skill decision-journal --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Decision Journal?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Decision Journal
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/synaptiai-decision-journal/badge)](https://www.skillsdirectory.com/skills/synaptiai-decision-journal)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: decision-journal
description: Extracts and structures development decisions from diffs, manages decision journal entries, and detects human gate triggers. Use when logging decisions during gh-start, gh-commit, or gh-address. Use when summarizing decisions for PR bodies or when checking for gate-triggering changes like new dependencies, security modifications, or scope deviations.
allowed-tools: Bash, Read, Grep, Glob
context: fork
agent: Explore
---

# Decision Journal

Captures, structures, and persists significant decisions made during AI-driven workflow execution. Uses post-hoc extraction from diffs rather than AI self-reporting during execution.

## Purpose

In a fully AI-driven workflow, decisions happen invisibly. This skill makes them visible by:
- Extracting decisions from completed diffs (what changed vs. what was expected)
- Detecting high-stakes changes that warrant human approval (gates)
- Structuring decisions into a machine-parseable journal format
- Condensing journals into PR-body-ready summaries

## Mode Routing

This skill operates in one of three modes. The calling command specifies the mode in its invocation prompt.

| Mode | When Used | What It Does |
|------|-----------|-------------|
| `init` | gh-start after branch creation | Creates journal file header |
| `log` | gh-start, gh-commit, gh-address after changes | Extracts decisions from diff + evaluates gate triggers |
| `summarize` | gh-pr during PR content generation | Condenses journal for PR body |

Read the mode from the invocation prompt and execute only that mode's instructions. If the mode is not one of `init`, `log`, or `summarize`, return an error: `"Unknown mode: {mode}. Expected one of: init, log, summarize."`

## Mode: init

**Input** (from invocation prompt): Issue number, branch name, issue title, issue body.

**Process:**

### Step 1: Read Configuration

```bash
# Read journal directory from settings (local > project > user > default)
JOURNAL_DIR=$(jq -r '.journal.dir // empty' .claude/settings.gh-workflow.local.json 2>/dev/null)
[ -z "$JOURNAL_DIR" ] && JOURNAL_DIR=$(jq -r '.journal.dir // empty' .claude/settings.gh-workflow.json 2>/dev/null)
[ -z "$JOURNAL_DIR" ] && JOURNAL_DIR=$(jq -r '.journal.dir // empty' "$HOME/.claude/settings.gh-workflow.json" 2>/dev/null)
[ -z "$JOURNAL_DIR" ] && JOURNAL_DIR=".decisions"
echo "Journal directory: $JOURNAL_DIR"
```

### Step 2: Generate Header

Generate the journal file header:

```markdown
# Decision Journal: Issue #{N} — {issue title}

**Issue**: #{N}
**Branch**: {branch-name}
**Started**: {YYYY-MM-DD}

---
```

**Output:** Return the header markdown and the resolved journal directory (from `.journal.dir` in settings, default `.decisions`). The calling command writes it to `{journal-dir}/issue-{N}.md`.

## Mode: log

**Input** (from invocation prompt): Description of what phase just completed (e.g., "task breakdown", "staged changes for commit", "addressed review feedback"). The calling command provides the relevant context.

**Process:**

### Step 1: Gather Context

```bash
# Get current branch and issue number
BRANCH=$(git branch --show-current)
ISSUE_NUM=$(echo "$BRANCH" | grep -oE 'issue-[0-9]+' | grep -oE '[0-9]+')

# Get the default branch
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
[ -z "$DEFAULT_BRANCH" ] && DEFAULT_BRANCH=$(git rev-parse --verify origin/main >/dev/null 2>&1 && echo "main" || echo "master")
```

```bash
# Read all comprehension config from settings (local > project > user > default)
# Single merge for both journal and gates — used in Steps 1-3
GHW_CONFIG=$(jq -n '
  def defaults: {
    gates: {
      newDependencies:"on", securityChanges:"on", schemaChanges:"on",
      apiSurfaceChanges:"on", scopeDeviations:"on", ambiguousRequirements:"on",
      customTriggers:[], customTriggersMode:"on"
    },
    journal: { dir:".decisions", sensitivityDefault:"public" }
  };
  defaults
    * (try input catch {})
    * (try input catch {})
    * (try input catch {})
' "$HOME/.claude/settings.gh-workflow.json" \
  ".claude/settings.gh-workflow.json" \
  ".claude/settings.gh-workflow.local.json" 2>/dev/null)

JOURNAL_DIR=$(echo "$GHW_CONFIG" | jq -r '.journal.dir')
SENSITIVITY_DEFAULT=$(echo "$GHW_CONFIG" | jq -r '.journal.sensitivityDefault')
echo "Journal directory: $JOURNAL_DIR"
echo "Default sensitivity: $SENSITIVITY_DEFAULT"
```

Use `$JOURNAL_DIR` instead of `.decisions` for all journal file paths in this mode. Use `$SENSITIVITY_DEFAULT` as the default sensitivity for new entries.

```bash
# Get the diff to analyze (staged changes for commit, or branch diff for other phases)
git diff --stat "$DEFAULT_BRANCH"...HEAD
git diff --name-only "$DEFAULT_BRANCH"...HEAD
```

```bash
# Get issue context
gh issue view "$ISSUE_NUM" --json title,body,labels 2>/dev/null
```

### Step 2: Extract Decisions (Post-Hoc)

Analyze the completed diff against the issue context. Identify decisions by comparing:
- **What changed** vs. **what the issue requested** — scope decisions, requirement interpretations
- **Patterns chosen** vs. **alternatives available** — architecture, implementation trade-offs
- **Files touched** vs. **expected impact area** — scope deviations

For each significant decision found, generate a journal entry:

```markdown
### {YYYY-MM-DD HH:MM} [{CATEGORY}] {Decision Title}

**Command**: {gh-start | gh-commit | gh-address}
**Decision**: {What was decided}
**Alternatives**: {What else was considered, or "N/A" for obvious choices}
**Rationale**: {Why this choice was made}
**Risk**: {Low | Medium | High | Critical}
**Sensitivity**: {public | internal}
**Gate**: {No — AI decision}
**References**: {#M, #K — cross-issue refs, or "None"}

---
```

**Categories**: `architecture`, `requirements`, `trade-off`, `implementation`, `risk`, `scope`

**Sensitivity rules:**
- Default: Use the `$SENSITIVITY_DEFAULT` value from Step 1 config (falls back to `public` if not configured)
- Use `internal` for decisions involving: security rationale, credential/secret handling, vulnerability remediation, access control logic
- Never document specific vulnerability details, exploitation vectors, previous insecure states, or secret values/locations — even in `internal` entries

### Step 3: Evaluate Gate Triggers

Extract gate configuration from the `$GHW_CONFIG` loaded in Step 1:

```bash
# Extract gates from the config already loaded in Step 1
echo "$GHW_CONFIG" | jq '.gates'
```

If no configuration found, all gates default to `on`.

Check the diff against these gate detection heuristics:

| Trigger | Detection Method | Config Key |
|---------|-----------------|------------|
| New dependency | New entries in `package.json`, `requirements.txt`, `Gemfile`, `go.mod`, `Cargo.toml`, or new git submodule | `.gates.newDependencies` |
| Security changes | Files matching `*auth*`, `*security*`, `*permission*`, `*token*`, `*secret*`, `*crypto*`, `*session*`; changes to `.env*`, CORS/TLS config | `.gates.securityChanges` |
| Schema changes | Database migration files, changes to `schema.*`, `*model*` definitions, API type definitions | `.gates.schemaChanges` |
| API surface changes | New route/endpoint definitions, changed function signatures in public modules, new command/skill files | `.gates.apiSurfaceChanges` |
| Scope deviations | Files modified outside the expected impact area from the issue | `.gates.scopeDeviations` |
| Ambiguous requirements | Acceptance criteria containing vague terms ("should be fast", "user-friendly", "appropriate"), contradictory criteria | `.gates.ambiguousRequirements` |

```bash
# Example detection for new dependencies
git diff "$DEFAULT_BRANCH"...HEAD --name-only | grep -E "(package\.json|requirements\.txt|Gemfile|go\.mod|Cargo\.toml|\.gitmodules)" 2>/dev/null
```

```bash
# Example detection for security-related files
git diff "$DEFAULT_BRANCH"...HEAD --name-only | grep -iE "(auth|security|permission|token|secret|crypto|session|\.env)" 2>/dev/null
```

```bash
# Evaluate custom trigger patterns (glob patterns from config)
CUSTOM_TRIGGERS=$(echo "$GHW_CONFIG" | jq -r '.gates.customTriggers[]' 2>/dev/null)
if [ -n "$CUSTOM_TRIGGERS" ]; then
  CHANGED_FILES=$(git diff "$DEFAULT_BRANCH"...HEAD --name-only)
  echo "$CUSTOM_TRIGGERS" | while read -r pattern; do
    echo "$CHANGED_FILES" | grep -E "$pattern" 2>/dev/null
  done
fi
```

Custom trigger matches use the `.gates.customTriggersMode` config key (`on`/`log`/`off`), following the same behavior as the named gates.

For each trigger that fires:
1. Check the corresponding config key
2. If `on` — include in gate trigger output
3. If `log` — log the decision entry with `Gate: Logged (auto-approved)` but do not include in gate triggers
4. If `off` — skip entirely

### Step 4: Return Output

**Output format:**

```markdown
## Decision Entries

{One or more journal entries in the schema format above}

## Gate Triggers

{If any gates fired with config = `on`:}

| Category | Trigger | Reason | Recommended Action | Alternatives |
|----------|---------|--------|--------------------|-------------|
| security-changes | gates.securityChanges | Modified auth middleware | Review security implications | Proceed without review |

{If no gates fired:}

No gate triggers detected.
```

The calling command:
- Appends decision entries to `{journal-dir}/issue-{N}.md` (using the resolved `$JOURNAL_DIR` from Step 1)
- If gate triggers are present, presents AskUserQuestion with the gate format
- Logs gate responses (approved/bypassed) back to the journal

## Mode: summarize

**Input** (from invocation prompt): Issue number, sensitivity filter preference.

**Process:**

1. Extract the issue number from the invocation prompt and read journal configuration:

```bash
# Extract issue number from invocation prompt (the calling command provides this)
# Example invocation: "Mode: summarize. Issue number: 42. Sensitivity filter: redact internal entries."
# Parse the issue number from the prompt text.

# Read journal-dir from settings (local > project > user > default)
JOURNAL_DIR=$(jq -r '.journal.dir // empty' .claude/settings.gh-workflow.local.json 2>/dev/null)
[ -z "$JOURNAL_DIR" ] && JOURNAL_DIR=$(jq -r '.journal.dir // empty' .claude/settings.gh-workflow.json 2>/dev/null)
[ -z "$JOURNAL_DIR" ] && JOURNAL_DIR=$(jq -r '.journal.dir // empty' "$HOME/.claude/settings.gh-workflow.json" 2>/dev/null)
[ -z "$JOURNAL_DIR" ] && JOURNAL_DIR=".decisions"

# Read the specific journal file using the issue number from the invocation prompt
cat "$JOURNAL_DIR/issue-$ISSUE_NUM.md" 2>/dev/null
```

**Note**: `$ISSUE_NUM` should be set from the issue number provided by the calling command in the invocation prompt (e.g., `"Mode: summarize. Issue number: 42."`). If not provided, fall back to branch detection: `ISSUE_NUM=$(git branch --show-current | grep -oE 'issue-[0-9]+' | grep -oE '[0-9]+')`.

2. Parse entries by splitting on `---` separators
3. For each entry:
   - If `Sensitivity: public` — include full entry in summary
   - If `Sensitivity: internal` — replace with: `[Internal decision — see decision journal for details]`
4. Condense into a summary format:

**Output format:**

```markdown
## Decision Summary

**Total decisions**: {N} ({M} public, {K} internal)
**Gates triggered**: {X} ({Y} approved, {Z} bypassed)

### Key Decisions

| # | Category | Decision | Risk |
|---|----------|----------|------|
| 1 | architecture | {title} | {risk} |
| 2 | requirements | {title} | {risk} |

### Decision Details

{For each public entry, include a condensed version:}

**{Category}: {Title}** — {Decision}. {Rationale}. Risk: {risk}.

{For internal entries:}

**{Category}: [Internal decision]** — [See decision journal for details]

### Gate Activity

{Summary of gate triggers and responses}
```

## Output Format

All modes return structured markdown. The calling command handles file I/O.

| Mode | Returns |
|------|---------|
| `init` | Journal file header markdown |
| `log` | Decision entries + gate trigger table |
| `summarize` | Condensed summary for PR body |

## Graceful Degradation

| Missing Capability | Fallback |
|-------------------|----------|
| No gate config in settings | All gates default to `on` |
| No issue context (gh issue view fails) | Extract decisions from diff only, skip requirement comparison |
| No existing journal file (for summarize) | Return "No decision journal found" notice |
| Empty diff | Return "No changes to analyze" with no entries |
| Branch has no issue number | Use branch name as identifier, skip issue context |

## Integration Points

This skill is invoked by:
- `gh-start` — Phase 2 (`init` mode), Phase 5 (`log` mode)
- `gh-commit` — Phase 2 (`log` mode)
- `gh-pr` — Phase 3 (`summarize` mode)
- `gh-address` — After feedback aggregation (`log` mode)

The calling command handles all file persistence (Write/Edit to `{journal-dir}/issue-{N}.md`, where `journal-dir` is read from settings via `.journal.dir`, default `.decisions`) and AskUserQuestion presentation for gate triggers.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.