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-workflowThis skill is limited to using the following tools:
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.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
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.