Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions cmd/entire/cli/checkpoint/committed.go
Original file line number Diff line number Diff line change
Expand Up @@ -1826,6 +1826,15 @@ func GetGitAuthorFromRepo(repo *git.Repository) (name, email string) {
return name, email
}

// CreateMigrationCommit creates a commit on behalf of Entire's migration or
// repair tooling. Author and committer are the real git user (so commit
// signing keeps working); the standard migration trailer block is appended to
// subject to carry the tool-produced provenance signal.
func CreateMigrationCommit(ctx context.Context, repo *git.Repository, treeHash, parentHash plumbing.Hash, subject string) (plumbing.Hash, error) {
authorName, authorEmail := GetGitAuthorFromRepo(repo)
return CreateCommit(ctx, repo, treeHash, parentHash, trailers.FormatMigration(subject), authorName, authorEmail)
}

// CreateCommit creates a git commit object with the given tree, parent, message, and author.
// If parentHash is ZeroHash, the commit is created without a parent (orphan commit).
func CreateCommit(ctx context.Context, repo *git.Repository, treeHash, parentHash plumbing.Hash, message, authorName, authorEmail string) (plumbing.Hash, error) {
Expand Down
7 changes: 5 additions & 2 deletions cmd/entire/cli/checkpoint/v2_committed.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/entireio/cli/cmd/entire/cli/jsonutil"
"github.com/entireio/cli/cmd/entire/cli/logging"
"github.com/entireio/cli/cmd/entire/cli/paths"
"github.com/entireio/cli/cmd/entire/cli/trailers"
"github.com/entireio/cli/cmd/entire/cli/validation"
"github.com/entireio/cli/cmd/entire/cli/versioninfo"
"github.com/entireio/cli/redact"
Expand Down Expand Up @@ -128,8 +129,10 @@ func (s *V2GitStore) WriteCommittedMainBatch(ctx context.Context, batch []WriteC
}
}

// One commit, one ref update for the entire batch.
commitMsg := fmt.Sprintf("Migrate batch: %d checkpoint(s), %d session(s)\n", len(groupOrder), len(batch))
// One commit, one ref update for the entire batch. WriteCommittedMainBatch
// is migration-only (see callers), so the message carries the standard
// migration trailer block alongside every other tool-produced commit.
commitMsg := trailers.FormatMigration(fmt.Sprintf("Migrate batch: %d checkpoint(s), %d session(s)", len(groupOrder), len(batch)))
last := batch[len(batch)-1]
authorName, authorEmail := last.AuthorName, last.AuthorEmail
if authorName == "" || authorEmail == "" {
Expand Down
33 changes: 13 additions & 20 deletions cmd/entire/cli/migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,7 @@ var (
)

const (
migrateRemoteName = "origin"
migrateAuthorName = "Entire Migration"
migrateAuthorEmail = "migration@entire.dev"
migrateRemoteName = "origin"
)

var migrateMaxCheckpointsPerGeneration = checkpoint.DefaultMaxCheckpointsPerGeneration
Expand Down Expand Up @@ -881,9 +879,8 @@ func writeMigratedFinalFullCurrent(ctx context.Context, repo *git.Repository, v2
return fmt.Errorf("build migrated full/current tree: %w", err)
}

commitHash, err := checkpoint.CreateCommit(ctx, repo, treeHash, parentHash,
"Write migrated partial generation\n",
migrateAuthorName, migrateAuthorEmail)
commitHash, err := checkpoint.CreateMigrationCommit(ctx, repo, treeHash, parentHash,
"Write migrated partial generation")
if err != nil {
return fmt.Errorf("create migrated full/current commit: %w", err)
}
Expand Down Expand Up @@ -975,9 +972,8 @@ func writeMigratedFullGeneration(ctx context.Context, repo *git.Repository, v2St
return plumbing.ZeroHash, fmt.Errorf("add generation metadata: %w", err)
}

commitHash, err := checkpoint.CreateCommit(ctx, repo, treeHash, plumbing.ZeroHash,
fmt.Sprintf("Archive migrated generation: %s\n", refName),
migrateAuthorName, migrateAuthorEmail)
commitHash, err := checkpoint.CreateMigrationCommit(ctx, repo, treeHash, plumbing.ZeroHash,
fmt.Sprintf("Archive migrated generation: %s", refName))
if err != nil {
return plumbing.ZeroHash, fmt.Errorf("create migrated generation commit: %w", err)
}
Expand Down Expand Up @@ -1144,9 +1140,8 @@ func ensureEmptyV2FullCurrent(ctx context.Context, repo *git.Repository) error {
return fmt.Errorf("build empty v2 full/current tree: %w", err)
}

commitHash, err := checkpoint.CreateCommit(ctx, repo, emptyTreeHash, plumbing.ZeroHash,
"Start generation\n",
migrateAuthorName, migrateAuthorEmail)
commitHash, err := checkpoint.CreateMigrationCommit(ctx, repo, emptyTreeHash, plumbing.ZeroHash,
"Start generation")
if err != nil {
return fmt.Errorf("create empty v2 full/current commit: %w", err)
}
Expand Down Expand Up @@ -1228,9 +1223,8 @@ func pruneV2CheckpointRef(ctx context.Context, repo *git.Repository, v2Store *ch
return nil
}

commitHash, err := checkpoint.CreateCommit(ctx, repo, newRoot, parentHash,
fmt.Sprintf("Reset checkpoint before force migration: %s\n", cpID),
migrateAuthorName, migrateAuthorEmail)
commitHash, err := checkpoint.CreateMigrationCommit(ctx, repo, newRoot, parentHash,
fmt.Sprintf("Reset checkpoint before force migration: %s", cpID))
if err != nil {
return fmt.Errorf("failed to create v2 prune commit for %s: %w", refName, err)
}
Expand Down Expand Up @@ -1284,9 +1278,8 @@ func pruneV2ArchivedCheckpointRef(ctx context.Context, repo *git.Repository, v2S
return fmt.Errorf("failed to recompute generation metadata for %s: %w", refName, err)
}

commitHash, err := checkpoint.CreateCommit(ctx, repo, newRoot, parentHash,
fmt.Sprintf("Reset checkpoint before force migration: %s\n", cpID),
migrateAuthorName, migrateAuthorEmail)
commitHash, err := checkpoint.CreateMigrationCommit(ctx, repo, newRoot, parentHash,
fmt.Sprintf("Reset checkpoint before force migration: %s", cpID))
if err != nil {
return fmt.Errorf("failed to create v2 prune commit for %s: %w", refName, err)
}
Expand Down Expand Up @@ -1391,8 +1384,8 @@ func buildMigrateWriteOpts(content *checkpoint.SessionContent, info checkpoint.C
TranscriptIdentifierAtStart: m.TranscriptIdentifierAtStart,
IsTask: m.IsTask,
ToolUseID: m.ToolUseID,
AuthorName: migrateAuthorName,
AuthorEmail: migrateAuthorEmail,
// Author left zero so WriteCommittedMainBatch falls back to the real
// git user; provenance lives in the migration trailer block.
}
}

Expand Down
10 changes: 2 additions & 8 deletions cmd/entire/cli/strategy/generation_repair.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@ import (
"github.com/go-git/go-git/v6/plumbing"
)

const (
repairAuthorName = "Entire Migration"
repairAuthorEmail = "migration@entire.dev"
)

// RepairV2GenerationMetadataResult describes archived v2 generation metadata
// repair work performed by RepairV2GenerationMetadata.
type RepairV2GenerationMetadataResult struct {
Expand Down Expand Up @@ -132,9 +127,8 @@ func repairOneV2GenerationMetadata(
return false, nil
}

newCommitHash, commitErr := checkpoint.CreateCommit(ctx, repo, newTreeHash, oldCommitHash,
fmt.Sprintf("Repair generation metadata: %s\n", candidate.Name),
repairAuthorName, repairAuthorEmail)
newCommitHash, commitErr := checkpoint.CreateMigrationCommit(ctx, repo, newTreeHash, oldCommitHash,
"Repair generation metadata: "+candidate.Name)
if commitErr != nil {
return false, fmt.Errorf("failed to create repair commit: %w", commitErr)
}
Expand Down
18 changes: 18 additions & 0 deletions cmd/entire/cli/trailers/trailers.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,17 @@ const (
// AgentTrailerKey identifies the agent that created a checkpoint.
// Format: human-readable agent name e.g. "Claude Code", "Cursor"
AgentTrailerKey = "Entire-Agent"

// MigrationTrailerKey marks a commit as produced by Entire's migration or
// repair tooling. The author/committer stay the real git user so commit
// signing keeps working; this trailer carries the provenance signal as a
// greppable boolean.
MigrationTrailerKey = "Entire-Migration"

// MigrationCoAuthor is the identity used in the Co-Authored-By trailer on
// migration/repair commits. GitHub renders it as a co-author chip, making
// the tool-produced nature visible without affecting signing attribution.
MigrationCoAuthor = "Entire Migration <migration@entire.io>"
)

// Pre-compiled regexes for trailer parsing.
Expand Down Expand Up @@ -246,6 +257,13 @@ func FormatShadowTaskCommit(message, taskMetadataDir, sessionID string) string {
return sb.String()
}

// FormatMigration creates a commit message with the standard migration
// trailer block: a Co-Authored-By line crediting the migration identity and
// the Entire-Migration provenance flag.
func FormatMigration(message string) string {
return fmt.Sprintf("%s\n\nCo-Authored-By: %s\n%s: true\n", message, MigrationCoAuthor, MigrationTrailerKey)
}
Comment thread
Soph marked this conversation as resolved.

// FormatCheckpoint creates a commit message with a checkpoint trailer.
// This links user commits to their checkpoint metadata on entire/checkpoints/v1 branch.
func FormatCheckpoint(message string, cpID checkpointID.CheckpointID) string {
Expand Down
Loading