Skip to content

feat: add trajectory compaction for PR merges#13

Merged
khaliqgant merged 8 commits intomainfrom
feat/pr-compaction
Feb 20, 2026
Merged

feat: add trajectory compaction for PR merges#13
khaliqgant merged 8 commits intomainfrom
feat/pr-compaction

Conversation

@khaliqgant
Copy link
Member

@khaliqgant khaliqgant commented Feb 19, 2026

Summary

Add trail compact command 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 compact

# Compact trajectories from the last 7 days
trail compact --since 7d

# Compact trajectories for a specific PR
trail compact --pr 123

# Compact specific trajectories
trail compact --ids traj_abc,traj_xyz

# Preview without saving
trail compact --since 7d --dry-run

Automatic GitHub Action

Workflow triggers on PR merge and runs compaction automatically:

on:
  pull_request:
    types: [closed]

jobs:
  compact:
    if: github.event.pull_request.merged
    steps:
      - run: npx trail compact --pr ${{ github.event.number }}

What Gets Compacted

  • Decision grouping by category (architecture, api, database, testing, security, performance, tooling, naming, compliance)
  • Key learnings aggregated across trajectories
  • Files affected consolidated
  • Commits linked
  • Date range tracked

Output

Compacted trajectories saved to .trajectories/compacted/ with structure:

  • Source trajectory IDs
  • Grouped decisions with reasoning
  • Summary statistics
  • Key learnings

Test plan

  • Build succeeds
  • trail compact --help shows options
  • trail compact --dry-run previews compaction
  • GitHub Action workflow syntax is valid

🤖 Generated with Claude Code


Open with Devin

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>
devin-ai-integration[bot]

This comment was marked as resolved.

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>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

@khaliqgant khaliqgant merged commit 850d247 into main Feb 20, 2026
6 checks passed
@khaliqgant khaliqgant deleted the feat/pr-compaction branch February 20, 2026 10:16
Copy link

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

View 15 additional findings in Devin Review.

Open in Devin Review

Comment on lines +218 to +225
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));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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:

  1. "Improve" contains the substring "pr" (at positions 2–3: I-m-p-r-o-v-e), satisfying the PR part.
  2. .* matches everything in between.
  3. "15" ends with "5" at a word boundary, satisfying 5\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.

Suggested change
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));
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

khaliqgant pushed a commit that referenced this pull request Feb 20, 2026
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant