feat(groomer): add title rewriting and body enrichment#490
Conversation
…peline Extend the hosted groomer to rewrite bad issue titles and enrich sparse issue bodies, with guardrail logic to prevent unnecessary changes. Title rewriting: - Only triggers when title is <10 chars, single-word generic token (P0, TODO, bug, fix), or clearly a priority/label token - Proposes imperative verb form, 10-200 chars, based on body content Body enrichment: - Only triggers when body is missing, empty, or <100 chars (excluding HTML/markdown comments) - Adds structured context without clobbering existing content Schema validation: - proposedTitle must be 10-200 chars - proposedBody must be under 10000 chars - Both optional with null tolerance Implementation: - Added updateIssueTitleAndBody() to GitHub API helpers - Updated LLM prompt with title/body enrichment instructions - Guardrail checks in run pipeline before applying mutations - Mutation plan tracks originalTitle, proposedTitle, proposedBody - appliedMutations includes titleUpdated and bodyUpdated flags - Full test coverage for guardrails and mutation application
There was a problem hiding this comment.
AI Automated Review
Full PR review.
Analysis engine: MiniMax-M2.7@https://litellm.jory.dev/v1 (anthropic) — routed smart (risk match: db_or_migration_changes)
PR Review: feat(groomer): add title rewriting and body enrichment
Recommendation: Approve
This PR adds title rewriting and body enrichment to the hosted groomer pipeline. The implementation is well-structured with comprehensive test coverage. CI (tests, lint, build, typecheck, Docker) passed.
Change-by-Change Findings
src/lib/github.ts (+28 lines)
Added updateIssueTitleAndBody() — a thin PATCH wrapper around the GitHub Issues API. Accepts title and body fields. Proper error handling with status-check and response text on failure. No secrets, no file serving, no auth-bypass concerns. Clean addition.
src/lib/groomer/llm.ts (+17, -2 lines)
Updated the LLM system prompt with:
- Title rewriting rules: Triggers only for bad titles (<10 chars, single-word generics like P0/TODO/bug/fix, or priority tokens). New title must be 10–200 chars, imperative verb, derived from body/labels/comments.
- Body enrichment rules: Triggers only for missing/empty/<100-char bodies. Adds structured context; append-only rather than clobbering existing content. Under 10,000 chars.
The prompts are precise and include explicit guardrails baked into the LLM instructions.
src/lib/groomer/schema.ts (+27, -1 lines)
Extended GroomerOutput with proposedTitle?: string and proposedBody?: string. Added validation:
proposedTitle: 10–200 chars, null-tolerantproposedBody: under 10,000 chars, null-tolerant- Non-string values are rejected with structured errors
src/lib/groomer/run.ts (+92, -2 lines)
Key logic additions:
shouldRewriteTitle(): returnstruefor empty, <10-char, or single-word generic tokens. Defensive — good titles are left alone.shouldEnrichBody(): returnstruefor null/empty or <100 visible chars (strips HTML/markdown comments).computeTitleBodyMutations(): combines LLM proposals with guardrail checks, only returning mutations that pass both.- Mutation application: title and body updates are called via
updateTitleAndBody()only when guardrails pass. Both can be applied in a single PATCH call. - Dry-run mode is respected — no GitHub API calls when
dryRun: true. - Both
appliedMutations.titleUpdatedandbodyUpdatedare set correctly.
src/lib/groomer/schema.test.ts (+94 lines)
10 new tests covering:
- Valid/invalid
proposedTitle(boundary: 10 and 200 chars) - Valid/invalid
proposedBody(boundary: 10,000 chars) - Null tolerance for both fields
- Rejects non-string values
- Both fields together
src/lib/groomer/run.test.ts (+186 lines)
10 new tests covering:
- Good titles are not rewritten
- Bad short titles (<10 chars) are rewritten
- Single-word generic titles (TODO, P0) are rewritten
- Empty titles are rewritten
- Substantial bodies are not enriched
- Sparse bodies are enriched
- Null bodies are enriched
- Both title rewrite + body enrichment applied together
- Dry-run includes mutation plan but skips API calls
- Skips update when LLM doesn't propose changes
Required Checks
✅ review migration for data loss risk
Does not apply. This PR contains no database migrations or schema changes. The changed files are exclusively application logic (github.ts, llm.ts, run.ts, schema.ts) and their tests. There is no Prisma migration, no prisma/schema.prisma change, and no data model modification. The db_or_migration_changes classification appears to be a false positive from the classifier; the PR only touches library code. No data loss risk exists in this change.
✅ test migration on a copy of production schema
Does not apply. As noted above, there are no migrations in this PR. Testing on a production schema copy is not relevant to this change.
Standards Compliance
From AGENTS.md conventions verified:
- ✅ Prisma schema — Not modified; no foreign key or relation changes
- ✅ API routes — No route changes; the
POST /api/groomer/runbehavior is extended via existing dependency injection without changing the route surface - ✅ Error handling —
updateIssueTitleAndBody()usesresponse.okcheck and throws meaningful errors with status + response text - ✅ Validation — Input validation is enforced at the schema level (
validateGroomerOutput) before mutations are applied - ✅ No secrets — No
.env, credentials, or tokens added - ✅ No agent-specific names in generic docs — N/A; no docs changed
Linked Issue Fit
No linked issue was provided in the corpus. The PR body describes the feature comprehensively with trigger conditions, guardrails, schema validation rules, file manifest, and example. Implementation aligns with the described behavior.
Evidence Provider Findings
No evidence providers configured for this PR.
Tool Harness Findings
No tool harness findings — the corpus was reviewed directly.
Unknowns / Needs Verification
None identified. The PR is self-contained, well-tested, CI-green, and the implementation logic is verifiable from the diff alone.
Conclusion
This is a solid feature addition. The groomer now rewrites bad issue titles and enriches sparse issue bodies with appropriate guardrails. The dual-layer protection (LLM prompt rules + application-level shouldRewriteTitle/shouldEnrichBody checks) ensures safe, non-destructive mutations. Comprehensive tests cover all paths including dry-run and combined mutations. No blockers.
Summary
Extend the hosted groomer (
POST /api/groomer/run) to rewrite bad issue titles and enrich sparse issue bodies, with guardrail logic to prevent unnecessary changes.Title Rewriting
Body Enrichment
Schema Validation
proposedTitle: 10-200 chars, optional with null toleranceproposedBody: under 10000 chars, optional with null toleranceFiles Changed
src/lib/github.tsupdateIssueTitleAndBody()API helpersrc/lib/groomer/schema.tsproposedTitle,proposedBodyto output + validationsrc/lib/groomer/llm.tssrc/lib/groomer/run.tssrc/lib/groomer/schema.test.tssrc/lib/groomer/run.test.tsExample
Issue title "P0" → rewritten to "Fix SSO/OIDC callback state verification mismatch causing 400 errors" based on body content.
Verification
npx tsc --noEmitpasses