Vizpy

Commit Log to User Changelog

Stop writing "fixed a null check." Start writing what actually broke.

Commit Log to User Changelog

Engineers write commit messages for engineers. Changelogs are for users. These are fundamentally different documents — but LLMs produce the former when asked for the latter.

Given a list of commits like:

fix: handle null user_id in session middleware
refactor: extract auth helper to shared module
feat: add retry logic to payment webhook handler

A model without optimization writes:

- Fixed null handling in session middleware
- Refactored authentication helpers
- Added retry logic to payment webhook

That's not a changelog. That's a lightly paraphrased commit log. Users don't know what "session middleware" is. They want to know: did something break? is it fixed? what can I do now that I couldn't before?

A real changelog entry for that first commit: "Fixed a crash that could occur when your session expired mid-checkout."

This example optimizes the translation from technical cause to user impact.

Optimizer: ContraPromptOptimizer Difficulty: Intermediate


Full Example

import dspy
import vizpy
 
dspy.configure(lm=dspy.LM("openai/gpt-4o-mini"))
 
 
class ChangelogEntry(dspy.Signature):
    """Translate a commit message into a user-facing changelog entry.
    Write from the user's perspective: what changed for them, not what the code does."""
 
    commit: str = dspy.InputField(desc="Git commit message with type prefix (fix/feat/refactor)")
    context: str = dspy.InputField(desc="Brief description of the affected feature area")
    entry: str = dspy.OutputField(
        desc="One sentence changelog entry. Start with the user impact, not the technical mechanism."
    )
 
 
module = dspy.Predict(ChangelogEntry)
 
 
_train_data = [
    {
        "commit": "fix: handle null user_id in session middleware",
        "context": "Checkout flow — affects users whose session expired mid-purchase",
        "good_entry": "Fixed a crash that could occur when your session expired during checkout.",
        "bad_pattern": "technical mechanism",
        "must_avoid": ["null", "middleware", "session middleware", "user_id"],
        "must_convey": "checkout crash fixed",
    },
    {
        "commit": "feat: add retry logic to payment webhook handler",
        "context": "Subscription billing — occasional failed payment notifications",
        "good_entry": "Subscriptions now recover automatically from temporary payment failures instead of requiring manual renewal.",
        "bad_pattern": "implementation detail",
        "must_avoid": ["retry logic", "webhook", "handler"],
        "must_convey": "subscription recovery",
    },
    {
        "commit": "fix: prevent race condition in concurrent file uploads",
        "context": "Document upload — affected users uploading multiple files simultaneously",
        "good_entry": "Fixed a bug where uploading multiple files at once could result in missing or duplicate files.",
        "bad_pattern": "technical jargon",
        "must_avoid": ["race condition", "concurrent", "thread"],
        "must_convey": "multiple file upload",
    },
    {
        "commit": "feat: implement exponential backoff for API rate limiting",
        "context": "Integrations — affects users who hit API limits on high-volume plans",
        "good_entry": "API integrations now handle rate limits gracefully instead of failing with an error.",
        "bad_pattern": "implementation",
        "must_avoid": ["exponential backoff", "rate limiting", "implement"],
        "must_convey": "API integration improvement",
    },
    {
        "commit": "fix: sanitize HTML in user-submitted comments",
        "context": "Community forums — potential display issues with special characters",
        "good_entry": "Fixed comment formatting issues caused by special characters like < and >.",
        "bad_pattern": "security terminology",
        "must_avoid": ["sanitize", "HTML", "XSS"],
        "must_convey": "comment display fix",
    },
    {
        "commit": "refactor: migrate auth token storage from localStorage to httpOnly cookies",
        "context": "Account security — background security improvement",
        "good_entry": "Improved account security by updating how login sessions are stored.",
        "bad_pattern": "implementation detail",
        "must_avoid": ["localStorage", "httpOnly", "cookie", "token storage", "refactor"],
        "must_convey": "security improvement",
    },
]
 
 
def metric(example, prediction):
    entry = prediction.entry.lower()
 
    # Check for technical jargon leaking through
    leaked_jargon = [term for term in example["must_avoid"] if term.lower() in entry]
 
    # Rough check that the user-facing concept is present
    convey_words = example["must_convey"].lower().split()
    conveys_impact = sum(1 for w in convey_words if w in entry) >= len(convey_words) * 0.5
 
    if not leaked_jargon and conveys_impact:
        score, success = 1.0, True
        feedback = ""
    elif leaked_jargon and conveys_impact:
        score, success = 0.5, False
        feedback = (
            f"The entry leaks technical terms: {', '.join(leaked_jargon)}. "
            f"Rewrite from the user's perspective — what changed for them, not what the code does. "
            f"Example of the right register: '{example['good_entry']}'"
        )
    else:
        score, success = 0.0, False
        feedback = (
            f"Entry doesn't convey the user impact ('{example['must_convey']}'). "
            f"Avoid: {', '.join(leaked_jargon or ['technical implementation language'])}. "
            f"Target register: '{example['good_entry']}'"
        )
 
    return vizpy.Score(
        value=score,
        is_success=success,
        feedback=feedback,
        error_type="jargon_leak" if leaked_jargon else ("missing_impact" if not conveys_impact else ""),
    )
 
 
optimizer = vizpy.ContraPromptOptimizer(metric=metric)
optimized = optimizer.optimize(module, _train_data)
 
 
# Test
result = optimized(
    commit="fix: resolve deadlock in database connection pool under high concurrency",
    context="Affects users on high-traffic plans who experienced random request failures",
)
print(result.entry)
# "Fixed intermittent request failures that affected high-traffic accounts during peak usage."

The Register Problem

The failure here isn't factual — it's register. Technical and user-facing writing describe the same event in different vocabularies:

TechnicalUser-facing
null pointer dereferencecrash
race conditionintermittent bug
HTTP 429 rate limitthe feature stopped working
XSS sanitizationdisplay issue
connection pool deadlockrequest failures

The model defaults to the technical register because commit messages are its input. ContraPromptOptimizer is well-suited here — it mines pairs where technical register leaked through vs. where it didn't, and extracts a rule about vocabulary substitution and perspective shift.

The metric's feedback field includes a concrete example of the target register (example['good_entry']). This is deliberate: when the feedback shows rather than just tells, the optimizer generates more specific, transferable rules.

On this page