Help us improve
Share bugs, ideas, or general feedback.
From gh-workflow
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 claudepluginhub synaptiai/synapti-marketplace --plugin gh-workflowHow this skill is triggered — by the user, by Claude, or both
Slash command
/gh-workflow:decision-journalThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
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.
Orchestrates GitHub issues/PRs/branches as knowledge graph for traceability in planning, brainstorming, designs, architectural decisions, session resumption. /shiplog.
Manages persistent progress state, module health, dependency graphs, and ADRs across sessions. Use for workflows needing durable tracking of decisions and status beyond single interactions.
Records significant architectural decisions like database choices, frameworks, or core patterns in docs/decisions.md. Activates on major decisions, explicit requests, or proactively at session ends.
Share bugs, ideas, or general feedback.
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.