Skip to content

Commit bdf5a4c

Browse files
committed
refactor(llm): simplify prompts, add concrete entity rule, fix type defaults
Removes Think-then-Compress instructions that caused token budget exhaustion on small models. Integrates concrete entity naming rule into system prompt Subject line. Simplifies user prompt to concise format. Adds bug evidence → fix inference and changes default fallback from Feat to Refactor.
1 parent 8db162d commit bdf5a4c

4 files changed

Lines changed: 21 additions & 19 deletions

File tree

src/domain/context.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,11 @@ SUGGESTED TYPE: {commit_type}{scope}
8989
DIFF:
9090
{diff}
9191
{constraints}{breaking}{metadata_breaking}{focus}
92-
Write a JSON commit message describing the changes shown in the diff.
93-
The subject must be specific and under {subject_budget} chars — describe WHAT was changed (e.g., "add system prompt to ollama provider", "update dependency versions").
94-
For the body: if the change is trivial (single rename, typo fix), use null. Otherwise write a short body (1-3 sentences) explaining WHY the change was made or what it enables.
95-
For breaking_change: only set this if existing users or dependents must change their code, config, or scripts to keep working — e.g., a public function/endpoint removed or renamed, a required parameter or field added, a config key changed. New optional features, bug fixes, and internal refactors are NOT breaking. Default to null.
92+
Subject must be under {subject_budget} chars and name at least one concrete entity (function, struct, variable) from the diff.
93+
Body: 1-3 sentences on WHY, or null if trivial. breaking_change: only if existing users must change code/config to stay compatible, else null.
9694
97-
Output format:
98-
{{"type": "<type>", "scope": {scope_json}, "subject": "<imperative verb + what changed>", "body": "<why this change was made, or null if trivial>", "breaking_change": null}}"#,
95+
Respond with ONLY this JSON:
96+
{{"type": "<type>", "scope": {scope_json}, "subject": "<imperative verb + what changed>", "body": null, "breaking_change": null}}"#,
9997
summary = self.change_summary,
10098
files = self.file_breakdown.trim(),
10199
commit_type = self.suggested_type.as_str(),

src/services/context.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ impl ContextBuilder {
168168
return CommitType::Build;
169169
}
170170

171+
// Explicit bug evidence -> fix
172+
if Self::detect_bug_evidence(changes) {
173+
return CommitType::Fix;
174+
}
175+
171176
// New public functions/structs -> feat (unless it's an API replacement)
172177
let has_new_public_symbols = symbols.iter().any(|s| {
173178
s.is_added
@@ -226,7 +231,7 @@ impl ContextBuilder {
226231
return CommitType::Refactor;
227232
}
228233

229-
CommitType::Feat
234+
CommitType::Refactor
230235
}
231236

232237
pub fn infer_scope(changes: &StagedChanges) -> Option<String> {

src/services/llm/mod.rs

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,25 @@ parameter added, a config key renamed. New optional additions, bug fixes, and in
1717
refactors are NOT breaking. Default to null.
1818
1919
Rules:
20-
- Subject: imperative, specific, lowercase start, no trailing period, max 72 chars total first line.
20+
- Subject: imperative, specific, lowercase start, no trailing period, max 72 chars total first line. Must name at least one concrete entity (function, struct, variable, file) from the diff.
2121
- Body: 1-3 sentences about WHY for non-trivial changes, else null.
2222
- Do not list files changed.
23-
- If the change is purely syntactic (collapsing nested blocks, reformatting, reordering imports) with identical behavior, use "style" — never describe it as fixing a bug or adding a feature.
23+
- If the change is purely syntactic (reformatting, reordering imports) with identical behavior, use "style".
2424
- The SUGGESTED TYPE is a hint. Override it if the diff clearly shows a different type.
25-
- Never copy labels, field names, or evidence tags from the prompt into your output. The breaking_change value must describe the actual change in plain English.
25+
- Never copy labels, field names, or evidence tags from the prompt into your output.
2626
- If public APIs are both added and removed, this is an API replacement (refactor), not a new feature.
2727
2828
Examples:
29-
GOOD: "add evidence-based commit validation with retry" — one specific thing
30-
GOOD: "replace path-only grouping with diff-shape fingerprinting" — names the concrete change
31-
BAD: "update code and improve things" — too vague
32-
BAD: "refactor code for better performance and add validation" — two concerns in one subject
29+
GOOD: "replace path-only grouping with diff-shape fingerprinting"
30+
GOOD: "add CommitValidator for evidence-based retry"
31+
BAD: "update code and improve things" — too generic
32+
BAD: "refactor code for better performance and add validation" — two concerns
3333
3434
1. Diff adds `pub fn new_api()` and removes `pub fn old_api()` → type: "refactor", breaking_change: "removed `old_api()`, use `new_api()` instead"
3535
2. Diff only changes whitespace/indentation → type: "style", body: null, breaking_change: null
3636
37-
Output ONLY valid JSON (nullable fields use null, not the string "null"):
37+
Respond with ONLY the JSON object, nothing else:
3838
{"type":"<type>","scope":null,"subject":"<subject>","body":null,"breaking_change":null}
39-
For scope, body, and breaking_change: replace null with a quoted string when applicable.
4039
"#;
4140

4241
pub mod anthropic;

tests/context.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ fn infer_type_more_deletions_is_refactor() {
394394
}
395395

396396
#[test]
397-
fn infer_type_default_fallback_is_feat() {
397+
fn infer_type_default_fallback_is_refactor() {
398398
// 30 insertions, 30 deletions (not small, not deletion-heavy, not special category)
399399
let changes = make_staged_changes(vec![make_file_change(
400400
"src/services/module.rs",
@@ -406,8 +406,8 @@ fn infer_type_default_fallback_is_feat() {
406406
let ctx = ContextBuilder::build(&changes, &[], &default_config());
407407
assert_eq!(
408408
ctx.suggested_type,
409-
CommitType::Feat,
410-
"large non-special change should fallback to Feat"
409+
CommitType::Refactor,
410+
"large non-special change should fallback to Refactor (safer than Feat)"
411411
);
412412
}
413413

0 commit comments

Comments
 (0)