
Pr Perfect
- 6 installs
- 2 repo stars
- Updated August 3, 2026
- othmanadi/pr-perfect
Helps with ai & agent building tasks during AI-assisted development.
About
pr-perfect is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- pr-perfect
- AI & Agent Building
- AI-coding skill
Pr Perfect by the numbers
- 6 all-time installs (skills.sh)
- Ranked #12,825 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/othmanadi/pr-perfect --skill pr-perfectAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 6 |
|---|---|
| repo stars | ★ 2 |
| Last updated | August 3, 2026 |
| Repository | othmanadi/pr-perfect ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Ship to Boss
Create PRs that serve as briefing documents — detailed enough for both humans and coding agents to review, verify, and merge with confidence.
Arguments
The skill accepts free-form ARGUMENTS at invocation. Only one flag needs special handling:
| Flag | Aliases | Effect |
|---|---|---|
--no-branch | --local-only, --no-pr, --draft-only | Skip branch creation, push, and `gh pr create` entirely. Write the engineered description to PR_DESCRIPTION.md at the repo root and stop. Use this when the user commits directly to main and wants the description text only — e.g. to paste into a PR opened manually, attach to a changelog, or hand off to someone else to create the PR. |
Detection: treat the flag as present if any of its aliases appears as a whole token in ARGUMENTS (case-insensitive). When present, Step 4 is replaced by the Local-only variant below — everything else (Steps 1–3) runs identically.
Why This Format Matters
The PR description is not just documentation. The reviewer (and their coding agent) uses it to verify changes make sense, spot conflicts, and confirm nothing breaks. Every table, every file listing, every verification bullet exists so the reviewer can cross-reference the description against the actual diff. A vague PR description forces the reviewer to reverse-engineer intent from raw code — that's slow and error-prone.
Workflow
Execute these steps in order. Do not skip any step.
Step 1: Forensic Commit Analysis
Study every commit that will be in the PR. This is the foundation — a lazy read here produces a lazy description.
# What commits are we working with?
git fetch origin
git log origin/main..HEAD --oneline --reverse
# For EACH commit, read the full message and file list
git show <sha> --stat --format="%s%n%n%b"
# Total diff summary
git diff origin/main..HEAD --statFor each commit, note:
- What problem it solves (not what it changes — WHY it changes)
- Which files it touches
- Whether it's a fix, feature, test, or refactor
Step 2: Group Into Themes
Commits rarely map 1:1 to PR sections. Look for natural groupings:
- Multiple commits touching the same subsystem = one section
- A fix + its test = one section
- An independent feature = its own section
Aim for 2-5 numbered sections. Each section needs a clear narrative arc: problem existed → here's what was broken → here's the fix → here's why it's safe.
Step 3: Write the PR Description
Read references/pr-examples.md for the exact style from the last merged PRs. Then write the description following this template structure:
Title Format
feat(scope): short theme 1, short theme 2, short theme 3Keep under 70 characters. Use the primary scope (e.g., ai, telemetry, test). Comma-separate if multiple themes. Use conventional commit prefix (feat, fix, test, refactor).
Body Structure
## Summary
One paragraph (2-3 sentences) stating what this PR does at the highest level.
Lead with the impact, not the implementation. Mention how many themes/areas are covered.
### 1. Section Title — Descriptive Subtitle
Start with the PROBLEM. What was broken, missing, or wrong? Use **bold** to highlight
the failure mode or gap. Be specific — "the web-search toggle silently vanished" not
"there was a bug in web search."
**Fix:** Describe the solution. If it involves a new pattern or contract, use a table:
| Column1 | Column2 | Column3 |
|---|---|---|
| data | data | data |
### 2. Next Section Title
Same pattern: problem → fix → evidence it's safe.
(repeat for each theme)
## Changes by file
| Layer | File | Changes |
|---|---|---|
| Backend | `file.rs` | +N: brief description of what changed |
| Frontend | `Component.tsx` | +N/−M: brief description |
Every file in the diff MUST appear in this table. Group by layer (Backend, Frontend,
Agents, Config, Tests). Use the `+N` / `+N/−M` format to show line counts.
## Commits
sha1 type(scope): commit message 1 sha2 type(scope): commit message 2 ...
List ALL commits in chronological order (oldest first). Use short SHA (7 chars).
## Verification
Bullet list of how to verify this works. Be specific:
- "All 11 previously failing tests now pass" (not "tests pass")
- "New config fields backward compatible (`#[serde(default)]`)" (not "backward compatible")
- "Zero changes to SSE contract" (not "nothing else changed")
**N files changed, +X, −Y. Safe to squash merge.**Step 4: Create Branch and PR (default)
Use this variant unless the user passed --no-branch (see Arguments section).
# Create feature branch from current HEAD
git checkout -b feature/<descriptive-name> HEAD
# Push to remote
git push -u origin feature/<descriptive-name>
# Write body to temp file first — avoids all shell quoting issues
# (single quotes, backticks, dollar signs in body content break heredoc-in-substitution)
cat > /tmp/pr_body.md <<'PREOF'
<body>
PREOF
# Create PR using body file — gh reads it directly, no shell parsing of content
gh pr create --base main --title "<title>" --body-file /tmp/pr_body.md
rm -f /tmp/pr_body.mdBranch naming: feature/<primary-theme> using kebab-case. Keep it short but descriptive.
After PR creation, switch back to main:
git checkout mainReport the PR URL to the user.
Step 4 (Local-only variant): --no-branch
Runs when ARGUMENTS contains --no-branch, --local-only, --no-pr, or --draft-only. Do NOT create a branch. Do NOT push. Do NOT call `gh pr create`. The user is working on main directly and only wants the engineered description on disk.
# Write the body to PR_DESCRIPTION.md at the repo root. Overwrites any prior
# draft. The title goes on the first line as an H1 so the file stands on its
# own if pasted into GitHub's web UI.
cat > PR_DESCRIPTION.md <<'PREOF'
# <title>
<body>
PREOFThen tell the user exactly three things: 1. The full path of PR_DESCRIPTION.md. 2. That no branch was created, no push happened, and no PR was opened. 3. How to turn it into a PR later if they want — typically: gh pr create --base main --head <their-branch> --title "<title>" --body-file PR_DESCRIPTION.md
Do NOT commit PR_DESCRIPTION.md. It is a scratch artifact for the user — add it to .gitignore only if the user asks. Leave git status dirty so they see it.
Style Rules
These rules are non-negotiable — they're what makes this team's PRs consistent.
1. German-enterprise tone: Technical, precise, no fluff. Tables over paragraphs when data is structured. Numbers over adjectives ("11 failures" not "several issues").
2. Problem-first sections: Every section starts with what was wrong. The reader should understand the pain before seeing the fix.
3. Bold failure modes: Use **bold** for the specific thing that broke or was missing. "The toggle silently vanished" — the bold text is what the reviewer's eye catches.
4. Pipe tables for structured data: Methods, tools, safety systems, file changes — anything with 2+ columns gets a markdown table.
5. File-by-file accountability: The "Changes by file" table is mandatory. Every file in the diff must be listed. This is what the reviewer's agent uses to cross-check.
6. Concrete verification: Each bullet in Verification must be falsifiable. The reviewer should be able to check each one by running a command or reading the diff.
7. Footer: Always end with the line count summary and "Safe to squash merge." (or explain why not, if applicable).
8. No Co-Authored-By: Never add co-author lines to commits or PR descriptions.
Edge Cases
- Single commit PR: Still use the full template. One numbered section is fine.
- Only test changes: Lead with what was broken in the test suite, not "added tests."
- Mixed fix + feature: Feature sections before fix sections (features are the headline).
- Large PRs (20+ files): Group the file table by subsystem, add a "Total" row.
PR Examples — Team Style Reference
---
PR #7
Title: feat(telemetry): complete observability stack — correlation IDs, Langfuse tool tracing, config seeding
Summary
Production-critical completion of the 3-system observability stack (correlation IDs, Langfuse LLM tracing, Langfuse infrastructure). The original implementation had working foundations but two integration gaps that left agent→backend correlation and tool-level Langfuse tracing inoperative in production. This PR closes both gaps across all 3 Go agents and the Rust backend, and seeds Langfuse configuration defaults for zero-guesswork admin setup.
Correlation ID chain — agent operations now traced end-to-end
The Go agents had correlation ID generation and HTTP header forwarding code, but every context.Background() created bare contexts — the X-Correlation-ID header was always empty, making the entire propagation pipeline dead code. All 3 agents now generate fresh UUID v4 correlation IDs at every operation boundary:
- ADFS Agent: 11 operation contexts replaced — config fetch, heartbeat, alert checks, rescan triggers, reconnect loops, event flushes, startup checks
- Scan Agent: heartbeat ticks, poll/dispatch cycles, and WebSocket connections now carry unique IDs per operation
- Updater Agent: heartbeat and WebSocket contexts instrumented
- New
ContextWithTimeout()/ContextWithCancel()helpers in all 3 telemetry packages — clean single-call API instead of verbose 3-line context composition - 6 new unit tests (2 per agent) verifying UUID generation, deadline propagation, and cancellation
Langfuse tool call tracing — every AI action now visible
ToolCallTrace was built and fully tested in llm_trace.rs but never imported or wired into the AI agent loop (ai_chat.rs). Langfuse showed LLM calls (prompt/response/tokens) but zero tool execution spans — meaning "which Cypher query ran and did it succeed" was invisible in the tracing dashboard. Now all 10 tool types emit Langfuse spans nested under each session trace:
| Tool | Captured |
|---|---|
execute_cypher | query input, row count, error status |
search_fulltext | index, term, result preview |
web_search | query, result count |
generate_artifact | type, title, success/failure |
generate_report | cypher, format, row count |
generate_pdf_document | title, format |
create_dashboard_app | title, format |
save_ownership_rule | rule type, name |
lookup_feature_catalog | query |
batch_queries | query count, total rows, error status |
Langfuse configuration seeding
Added INSERT OR IGNORE defaults for langfuse_enabled, langfuse_host, langfuse_public_key, langfuse_secret_key in config_db_schema.sql — admins can discover required keys without raw SQL guesswork. Idempotent on every startup, disabled by default.
Changes by layer
Go Agents (ADFS, Scan, Updater)
| File | Changes |
|---|---|
*/internal/telemetry/telemetry.go (×3) | ContextWithTimeout(), ContextWithCancel() helpers |
*/internal/telemetry/telemetry_test.go (×3) | 6 new tests for context helpers |
ADFS_Agent/cmd/adfs-agent/main.go | 11 bare contexts → correlation-ID-bearing contexts, telemetry import |
Scan_Agent/cmd/scan-agent/main.go | Heartbeat/poll/WebSocket contexts instrumented, telemetry import |
Updater_Agent/cmd/updater-agent/main.go | Heartbeat/WebSocket contexts instrumented, telemetry import |
Backend (Rust)
| File | Changes |
|---|---|
ai_chat.rs | Import ToolCallTrace, wire into all 10 tool call match arms |
config_db_schema.sql | Seed 4 Langfuse config defaults (disabled) |
Verification
- All 27 Go telemetry tests pass (9 per agent, including 6 new)
- All 3 Go agents compile clean (
go build ./...) - Rust backend compiles clean (
cargo check) - Zero behavioral changes — all additive instrumentation
- Langfuse remains disabled by default until admin configures credentials
Merge notes
- No conflicts expected — changes are isolated to telemetry infrastructure and the tool-call match block in
ai_chat.rs - No deployment steps required — Langfuse stays disabled until admin runs
infrastructure/langfuse/setup.ps1and sets credentials - Safe to squash merge — all changes are additive, no destructive refactors
---
PR #8
Title: feat(ai): agent loop safety rails, orchestrator prompt fix, Langfuse exit tracking
Summary
Production-critical hardening of the AI agent loop and prompt architecture. Three interconnected improvements that make the agent safe, observable, and correctly configured.
1. Orchestrator Prompt — Single Source of Truth
The orchestrator prompt (the short behavioral directive at the end of the system prompt) was defined in 3 places — i18n hardcode, config DB, and prompts DB — but the chat UI only used the i18n one, making the admin config page's setting completely inert. The per-message textarea below the chat input kept resetting on every new tab and could never be permanently cleared (backend had a hardcoded fallback).
Fix: Removed the chat-level textarea entirely. The backend now reads the orchestrator prompt server-side with a clean priority chain: per-request override → admin config DB (ai_orchestrator_prompt) → seeded prompts DB default. The admin config page is now the single source of truth. Also fixed the format suffix (HTML/PDF/web-search instructions) being duplicated in both the user message AND the orchestrator prompt — it now only appears in the user message.
2. Agent Loop Safety Rails — 7 Systems
The agent loop (run_agent_loop in ai_chat.rs) had no context management, no budget enforcement, no timeout, and no circuit breaker. Complex queries could run for 50+ minutes with unbounded token consumption.
| Safety System | What It Does |
|---|---|
| Reasoning block sanitization | Strips reasoning_content from choice before it enters message history (o3/o4/gpt-5.x thinking blocks were silently inflating context) |
| Wall-clock timeout | Kills the loop after 10 min (configurable via ai_agent_timeout_secs) |
| Token budget | Stops when total tokens exceed limit (configurable via ai_max_tokens_per_run, default unlimited) |
| Wrap-up warning | At 80% of any limit, injects a "wrap it up" system message so the LLM produces a complete answer instead of getting cut off |
| Per-tool call limits | Caps each tool (e.g. execute_cypher max 30, generate_artifact max 3) — pushes error result and skips execution when exceeded |
| Circuit breaker | After 5 consecutive tool failures (configurable), injects "use existing data" message instead of hammering a dead service |
| Context compaction | New compact_assistant_history() truncates older assistant messages (head 300 + tail 150 chars) while preserving tool_calls for API pairing |
All systems are purely additive — the agent always works, tools/prompts/SSE contract unchanged. The only difference is optimization and safety.
3. Langfuse Exit Tracking
The Langfuse trace output was missing the status_label, so the dashboard couldn't distinguish why an agent run ended. Fixed:
- Added
status_labelandexit_reasonto trace output JSON - Added
agent_completion_qualityscore: 1.0 for normal completion, 0.5 for safety-rail exits (timeout, token budget, max iterations) - Bug fix:
choicevariable neededmutfor reasoning block sanitization to compile
Changes by file
| File | Changes |
|---|---|
backend/src/api/ai_chat.rs | +338 lines: all 7 safety systems, reasoning sanitization, Langfuse exit tracking, orchestrator prompt resolution chain, compact_assistant_history() |
backend/src/api/ai_config.rs | +42 lines: 3 new config fields (struct, default, load, save) |
frontend/src/pages/AiConfigPage.tsx | +109 lines: 3 new admin UI controls (timeout, token budget, error limit) |
frontend/src/pages/AiAssistantPage.tsx | -35/+12 lines: removed orchestrator textarea, added warning step rendering (amber AlertTriangle) |
frontend/src/lib/api.ts | +8 lines: 3 new config fields in TypeScript interface, orchestratorPrompt made optional |
New Admin Config Controls
Under "Agent-Limits" section in AI Config page:
- Agent-Timeout (Sekunden): Default 600s (10 min), range 30-3600
- Max Tokens pro Lauf: Default unlimited, range 50K-2M
- Fehler-Abbruchgrenze: Default 5, range 2-20
Verification
cargo check— cleannpx tsc --noEmit— clean- All new fields backward compatible (
#[serde(default)], all default to 0 = "use code default") - SSE contract unchanged — existing frontends work without update
- Langfuse remains disabled by default until admin configures credentials
Commits
bf1f3ca fix(ai): fix reasoning sanitization mutability and add Langfuse exit tracking
b1405b6 feat(ai): add 7 agent loop safety rails
b71738c fix(ai): single source of truth for orchestrator promptSafe to squash merge.
---
PR #10
Title: feat(ai): web search config fix, test infrastructure sweep, Langfuse tracing upgrade
Summary
Three layers of production hardening: a user-facing web search config bug that silently hid features, a test infrastructure sweep that resolves all 11 failing tests, and a Langfuse tracing upgrade that adds full API field coverage with observation nesting.
1. Web Search Config — is_enabled vs is_ready
Selecting "Bing Search API" as the web search provider caused the web-search toggle to silently vanish from the chat UI. Root cause: WebSearchConfig::is_enabled() conflated admin intent (show the feature) with provider readiness (API key configured). When the Bing API key was missing, is_enabled() returned false, hiding the entire feature instead of showing a "not configured" warning.
Fix: Split into a 3-method contract:
| Method | Purpose | Consumers |
|---|---|---|
is_enabled() | Admin turned it on + valid provider selected | Frontend UI visibility |
is_ready() | Enabled AND provider prerequisites met (API key, etc.) | Runtime tool injection in ai_chat.rs |
config_error() | User-facing diagnostic when enabled but not ready | Frontend warning banner |
The frontend now shows the web-search option with an amber warning state when enabled but not ready, with inline config error text and provider-specific setup hints (e.g. Credential Manager key name for Bing Search API). bulk.rs sends aiWebSearchAvailable, aiWebSearchReady, and aiWebSearchConfigError to the frontend.
2. Test Infrastructure — All 11 Failures Resolved
The test suite had accumulated 11 failures across 8 files from API signature changes, role model updates, and missing service registrations.
API contract drift (6 files):
contract_tests.rs: enrollment token API changed toconsume_enrollment_token(id, agent_id, ip)— test still used old signaturecredentials.rs,workers.rs,system_architecture.rs:Role::Viewerremoved/renamed toRole::Requester— 7 permission-denied tests referenced a non-existent roledeep_report.rs,fs_cleanup.rs:{guid}in format strings triggered compiler warnings (needed{{guid}})
Infrastructure gaps (8 files):
TestAppmissingWorkerRegistryandWsConnectionManager— handlers crashed before permission checks could run (4 tests)global_search,temporal,system_architecture: requiredNeo4jPooleven for permission-check tests — changed toOption<Neo4jPool>(5 tests)provisioning: test referenced non-existent/templates/userroutecredentials: test was environment-specific (domain-joined machines returned different credential names)scheduler: cron day-of-week convention wrong (1=Sun not 1=Mon)config_db: fresh-table assertion failed because AI safety rail defaults now seed rows at startup
3. Safety Rail Test Coverage
Unit tests for the compaction and config systems added in PR #8:
| Test area | Coverage |
|---|---|
compact_tool_history | Noop within budget, truncates old results, skips small results (< 600 chars), preserves recent results |
compact_assistant_history | Noop with few messages, truncates old assistant content, preserves tool_calls intact for API pairing |
ai_config safety rails | Default values (0 = use code default), custom value loading, invalid value fallback to 0, JSON serde roundtrip (frontend ↔ backend contract) |
web_search | is_enabled (UI visibility), is_ready (runtime gating), config_error (diagnostics), frontend warning state |
4. Langfuse Tracing Upgrade — Full API Coverage + Nesting
The Langfuse client was sending minimal trace/generation/span events, missing most of the API's fields. Upgraded to full coverage:
New fields: tags, release, version, public, environment on traces. parent_observation_id, prompt_name, prompt_version on generations and spans. Cost override fields on usage. New EventEvent struct for discrete point-in-time observations.
Observation nesting: Tool call spans are now nested under their parent LLM generation via parent_observation_id — the Langfuse dashboard shows the full call tree instead of flat spans.
Wiring in `ai_chat.rs`: Traces tagged with context/mode, release from build consts, environment from langfuse_environment config key. LLM messages passed as generation input for full prompt visibility in the dashboard.
Changes by file
| Layer | File | Changes |
|---|---|---|
| Backend | web_search.rs | +162: is_enabled/is_ready/config_error 3-method split |
| Backend | ai_chat.rs | +273: web search is_ready() gating, safety rail tests, Langfuse nesting |
| Backend | ai_config.rs | +62: safety rail config test coverage |
| Backend | bulk.rs | +4: send aiWebSearchReady + aiWebSearchConfigError to frontend |
| Backend | telemetry/langfuse.rs | +383: full API field coverage, EventEvent, cost overrides |
| Backend | telemetry/llm_trace.rs | +357: builder upgrades, parent nesting, environment tagging |
| Backend | main.rs | +7: build release const for Langfuse trace tagging |
| Backend | config_db_schema.sql | +1: langfuse_environment seed |
| Backend | test_helpers.rs | +13: WorkerRegistry + WsConnectionManager in TestApp |
| Backend | 6 test fix files | +21/−19: role rename, API signature alignment, format string escaping |
| Backend | 4 infra fix files | +80/−27: Option<Neo4jPool>, cron fix, credential env-agnostic, config_db seed |
| Frontend | OutputFormatPanel.tsx | +69: amber warning state for unconfigured web search |
| Frontend | WebSearchPopup.tsx | +28: amber globe icon + diagnostic banner |
| Frontend | OutputFormatPanel.test.tsx | +32: warning state + clean state test cases |
| Frontend | api.ts | +6: aiWebSearchReady, aiWebSearchConfigError types |
| Frontend | AiAssistantPage.tsx | +5: pass new web search state props |
| Frontend | AiConfigPage.tsx | +21: provider-specific setup hints |
Commits
b9e07d6 fix(ai): split WebSearchConfig::is_enabled into is_enabled/is_ready/config_error
caa8ec1 feat(ui): show web-search with warning state when provider not ready
5eeac14 test(ai): update web search tests for is_enabled/is_ready contract
2b9f032 fix(tests): align test assertions with updated API signatures and role model
abef0a8 test(ai): add unit tests for agent loop safety rails and config fields
6fb2620 fix(test): resolve all 11 test failures with infrastructure fixes
036e39b feat(telemetry): upgrade Langfuse tracing with full API fields, nesting, and event supportVerification
- All 11 previously failing tests now pass
- Safety rail compaction + config roundtrip tests pass
- Web search warning state renders correctly in OutputFormatPanel and WebSearchPopup
- Langfuse events include full field set when tracing is enabled
- All new fields backward compatible (
#[serde(default)]) - Zero changes to SSE contract, tool definitions, or prompt system
27 files changed, +1441, −116. Safe to squash merge.