plugins/gh-workflow/skills/decision-journal/SKILL.md
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.
npx skillsauth add synaptiai/synapti-marketplace decision-journalInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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.
In a fully AI-driven workflow, decisions happen invisibly. This skill makes them visible by:
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."
Input (from invocation prompt): Issue number, branch name, issue title, issue body.
Process:
# 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"
Generate the journal file header:
# 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.
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:
# 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")
# 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.
# 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
# Get issue context
gh issue view "$ISSUE_NUM" --json title,body,labels 2>/dev/null
Analyze the completed diff against the issue context. Identify decisions by comparing:
For each significant decision found, generate a journal entry:
### {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:
$SENSITIVITY_DEFAULT value from Step 1 config (falls back to public if not configured)internal for decisions involving: security rationale, credential/secret handling, vulnerability remediation, access control logicinternal entriesExtract gate configuration from the $GHW_CONFIG loaded in Step 1:
# 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 |
# 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
# 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
# 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:
on — include in gate trigger outputlog — log the decision entry with Gate: Logged (auto-approved) but do not include in gate triggersoff — skip entirelyOutput format:
## 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:
{journal-dir}/issue-{N}.md (using the resolved $JOURNAL_DIR from Step 1)Input (from invocation prompt): Issue number, sensitivity filter preference.
Process:
# 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]+').
--- separatorsSensitivity: public — include full entry in summarySensitivity: internal — replace with: [Internal decision — see decision journal for details]Output format:
## 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}
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 |
| 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 |
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.
tools
Validate a FlowWorkflow YAML at `plugins/flow/workflows/<id>.workflow.yaml` against `schemas/v1/workflow.schema.json` AND cross-reference the referenced skills/agents exist + every Tier 3 action is confirm-gated + no native /goal or /loop dependency is declared. Use when /flow:workflow validate is invoked, when CI runs the workflow schema gates, or when a new workflow is being authored. This skill MUST be consulted because schema validation alone catches shape errors; cross-reference validation catches the silent-correctness failures (typo'd skill name, Tier 3 escape, /goal dependency) that would otherwise ship to users.
tools
Verify UI-facing changes by running a screenshot-analyze-verify loop across configured viewports, with a browser-tool priority cascade (Playwright MCP → Chrome DevTools MCP → CLI fallback → external skill fallback) and bounded iteration. Use after build/runtime verification passes and the diff includes `.tsx`/`.jsx`/`.vue`/`.html`/`.css`/`.scss`/`.svelte` files OR the acceptance criteria mention UI/page/render/display/visual. This skill MUST be consulted because UI changes that pass build and unit tests can still ship blank pages, render-blocking console errors, or broken responsive layouts that no other verification phase catches.
data-ai
Coordinate agent teams for adversarial review (paired skeptic/verifier per facet, challenge round with disposition vocabulary, consolidated findings with confidence) or parallel implementation (task sizing 5-6 per teammate, non-overlapping files). Enforces independent analysis before shared conclusions. Reference only (`disable-model-invocation: true`); loaded only when `agentTeams: true` in settings.
development
Conduct two-stage code review: Stage 1 verifies spec compliance (criterion-to-code mapping), Stage 2 evaluates security, correctness, performance, and maintainability across 6 parallel facets with P1/P2/P3 synthesis and deduplication by file:line. Use when reviewing code changes or pull requests. This skill MUST be consulted because reviewing quality on broken logic is wasted effort, and unmet acceptance criteria must block merge.