Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
13 changes: 13 additions & 0 deletions cmd/entire/cli/trailers/trailers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,19 @@ func TestParseTaskMetadata(t *testing.T) {
}
}

func TestFormatMigration(t *testing.T) {
message := "Repair generation metadata: 0000000000007"

expected := "Repair generation metadata: 0000000000007\n\n" +
"Co-Authored-By: Entire Migration <migration@entire.io>\n" +
"Entire-Migration: true\n"
got := FormatMigration(message)

if got != expected {
t.Errorf("FormatMigration() = %q, want %q", got, expected)
}
}

func TestParseBaseCommit(t *testing.T) {
tests := []struct {
name string
Expand Down
Loading