feat: add trajectory compaction for PR merges#13
Conversation
Add `trail compact` command to consolidate multiple trajectories into a summarized form with grouped decisions. Useful for reducing context accumulation after PR merges. Features: - `--since <date>` - Compact trajectories since date (ISO or relative like "7d") - `--pr <number>` - Compact trajectories associated with a PR - `--ids <ids>` - Compact specific trajectory IDs - `--dry-run` - Preview without saving Compaction groups similar decisions by category (architecture, api, database, testing, security, performance, tooling, naming, compliance). Also adds GitHub Action workflow that automatically runs compaction when PRs are merged to main. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Updates to trail compact command: - Default now only compacts trajectories that haven't been compacted yet - Add --branch flag to compact trajectories with commits not in target branch - Add --all flag to include previously compacted trajectories - Track compaction state via compactedInto field in index Example usage: trail compact # Uncompacted only (default) trail compact --branch main # Commits not in main trail compact --all # Include already compacted Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
| const prPattern = new RegExp( | ||
| `#${escaped}\\b|PR.*${escaped}\\b`, | ||
| "i", | ||
| ); | ||
| const matchesPR = | ||
| prPattern.test(trajectory.task.title) || | ||
| prPattern.test(trajectory.task.description || "") || | ||
| trajectory.commits.some((c) => prPattern.test(c)); |
There was a problem hiding this comment.
🔴 Overly broad --pr regex causes false-positive trajectory matching
The regex used to match trajectories by PR number is #${escaped}\b|PR.*${escaped}\b with the i (case-insensitive) flag. The PR.*${escaped}\b branch is far too permissive: PR with i matches any two-letter substring "pr" found in common English words (Improve, Express, Properly, Present, Provide, etc.), and .* allows any characters between that substring and the PR number.
Root cause and impact
For example, with --pr 5, the pattern becomes #5\b|PR.*5\b (case-insensitive). This matches a trajectory titled "Improve error handling for module 15" because:
"Improve"contains the substring"pr"(at positions 2–3: I-m-p-r-o-v-e), satisfying thePRpart..*matches everything in between."15"ends with"5"at a word boundary, satisfying5\b.
For single-digit PR numbers (1–9), nearly every trajectory whose title or description contains a word with "pr" and any occurrence of that digit would be falsely matched. This is especially impactful in the GitHub Actions workflow (compact-on-merge.yml:43) which falls back to --pr ${{ github.event.pull_request.number }} — for early PRs in a repository this would compact unrelated trajectories together, producing an incorrect summary.
Expected: Only trajectories explicitly referencing the PR (e.g., "PR #5", "#5") are matched.
Actual: Trajectories with incidental occurrences of "pr" (in common words) and the digit anywhere later in the text are matched.
| const prPattern = new RegExp( | |
| `#${escaped}\\b|PR.*${escaped}\\b`, | |
| "i", | |
| ); | |
| const matchesPR = | |
| prPattern.test(trajectory.task.title) || | |
| prPattern.test(trajectory.task.description || "") || | |
| trajectory.commits.some((c) => prPattern.test(c)); | |
| const prPattern = new RegExp( | |
| `#${escaped}\\b|\\bPR\\s*#?\\s*${escaped}\\b`, | |
| "i", | |
| ); | |
| const matchesPR = | |
| prPattern.test(trajectory.task.title) || | |
| prPattern.test(trajectory.task.description || "") || | |
| trajectory.commits.some((c) => prPattern.test(c)); |
Was this helpful? React with 👍 or 👎 to provide feedback.
The previous regex `PR.*${escaped}\b` was matching words containing "pr"
(e.g., "Improve", "Express") because the case-insensitive flag made "PR"
match any "pr" substring. Combined with `.*` allowing any characters,
this caused false-positive trajectory matching.
For example, `--pr 5` would match "Improve error handling for module 15"
because "Improve" contains "pr" and "15" contains "5".
Fix: Use `\bPR\s*#?\s*${escaped}\b` which requires "PR" at a word boundary
and properly handles variations like "PR #5", "PR#5", "PR 5".
Fixes Devin review comment on PR #13.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
Add
trail compactcommand to consolidate multiple trajectories into a summarized form with grouped decisions. Reduces context accumulation after PR merges.Problem
As trajectories accumulate, the context becomes noisy. Similar decisions across multiple trajectories need to be consolidated for better understanding.
Solution
CLI Command:
trail compactAutomatic GitHub Action
Workflow triggers on PR merge and runs compaction automatically:
What Gets Compacted
Output
Compacted trajectories saved to
.trajectories/compacted/with structure:Test plan
trail compact --helpshows optionstrail compact --dry-runpreviews compaction🤖 Generated with Claude Code