From 0808d270c1b96386c15e6e19328e908eb12a1af6 Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Thu, 14 May 2026 13:31:07 +0200 Subject: [PATCH 1/5] migrate: stamp commits with real git user, mark provenance via trailer The previous "Entire Migration " identity had two problems: we don't own entire.dev, and a fixed tooling identity blocks any future plan to sign migration/repair commits (signing keys are tied to real identities). Every migration and generation-repair commit now uses the migrating user's git user.name/user.email as author and committer, with a trailer block carrying the "this was tooling" signal: Co-Authored-By: Entire Migration Entire-Migration: true A single MigrationCommitMessage helper in the checkpoint package keeps the trailer format consistent across all seven commit sites (five CreateCommit calls in migrate.go, the batched /main write in WriteCommittedMainBatch, and the repair commit in generation_repair.go). buildMigrateWriteOpts no longer hard-codes the author on WriteCommittedOptions; the batch writer's existing GetGitAuthorFromRepo fallback now applies to migration too. Co-Authored-By: Claude Opus 4.7 (1M context) Entire-Checkpoint: 589c91d5f39e --- cmd/entire/cli/checkpoint/committed.go | 17 ++++++++++ cmd/entire/cli/checkpoint/v2_committed.go | 7 ++-- cmd/entire/cli/migrate.go | 35 +++++++++++--------- cmd/entire/cli/strategy/generation_repair.go | 10 ++---- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/cmd/entire/cli/checkpoint/committed.go b/cmd/entire/cli/checkpoint/committed.go index 69a2f767e..ae9bbc217 100644 --- a/cmd/entire/cli/checkpoint/committed.go +++ b/cmd/entire/cli/checkpoint/committed.go @@ -1826,6 +1826,23 @@ func GetGitAuthorFromRepo(repo *git.Repository) (name, email string) { return name, email } +// MigrationCoAuthorTrailer marks a commit as produced by Entire's migration or +// repair tooling. The author/committer remain the real git user (so commit +// signing keeps working); this trailer carries the provenance signal. +const MigrationCoAuthorTrailer = "Co-Authored-By: Entire Migration " + +// MigrationProvenanceTrailer is a greppable boolean flag that distinguishes +// tool-produced commits from user work without parsing the co-author email. +const MigrationProvenanceTrailer = "Entire-Migration: true" + +// MigrationCommitMessage wraps subject with the standard migration trailers. +// All migrate.go and generation_repair.go commit sites use this so the +// provenance signal stays consistent across every tool-produced commit. +func MigrationCommitMessage(subject string) string { + subject = strings.TrimRight(subject, "\n") + return fmt.Sprintf("%s\n\n%s\n%s\n", subject, MigrationCoAuthorTrailer, MigrationProvenanceTrailer) +} + // 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) { diff --git a/cmd/entire/cli/checkpoint/v2_committed.go b/cmd/entire/cli/checkpoint/v2_committed.go index 35edf2273..00cf85359 100644 --- a/cmd/entire/cli/checkpoint/v2_committed.go +++ b/cmd/entire/cli/checkpoint/v2_committed.go @@ -128,8 +128,11 @@ 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); the message carries the standard + // migration trailers so provenance is consistent across every migration + // commit, including this batched /main write. + commitMsg := MigrationCommitMessage(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 == "" { diff --git a/cmd/entire/cli/migrate.go b/cmd/entire/cli/migrate.go index 38ce8b19f..c3447f6ec 100644 --- a/cmd/entire/cli/migrate.go +++ b/cmd/entire/cli/migrate.go @@ -383,9 +383,7 @@ var ( ) const ( - migrateRemoteName = "origin" - migrateAuthorName = "Entire Migration" - migrateAuthorEmail = "migration@entire.dev" + migrateRemoteName = "origin" ) var migrateMaxCheckpointsPerGeneration = checkpoint.DefaultMaxCheckpointsPerGeneration @@ -881,9 +879,10 @@ func writeMigratedFinalFullCurrent(ctx context.Context, repo *git.Repository, v2 return fmt.Errorf("build migrated full/current tree: %w", err) } + authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, treeHash, parentHash, - "Write migrated partial generation\n", - migrateAuthorName, migrateAuthorEmail) + checkpoint.MigrationCommitMessage("Write migrated partial generation"), + authorName, authorEmail) if err != nil { return fmt.Errorf("create migrated full/current commit: %w", err) } @@ -975,9 +974,10 @@ func writeMigratedFullGeneration(ctx context.Context, repo *git.Repository, v2St return plumbing.ZeroHash, fmt.Errorf("add generation metadata: %w", err) } + authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, treeHash, plumbing.ZeroHash, - fmt.Sprintf("Archive migrated generation: %s\n", refName), - migrateAuthorName, migrateAuthorEmail) + checkpoint.MigrationCommitMessage(fmt.Sprintf("Archive migrated generation: %s", refName)), + authorName, authorEmail) if err != nil { return plumbing.ZeroHash, fmt.Errorf("create migrated generation commit: %w", err) } @@ -1144,9 +1144,10 @@ func ensureEmptyV2FullCurrent(ctx context.Context, repo *git.Repository) error { return fmt.Errorf("build empty v2 full/current tree: %w", err) } + authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, emptyTreeHash, plumbing.ZeroHash, - "Start generation\n", - migrateAuthorName, migrateAuthorEmail) + checkpoint.MigrationCommitMessage("Start generation"), + authorName, authorEmail) if err != nil { return fmt.Errorf("create empty v2 full/current commit: %w", err) } @@ -1228,9 +1229,10 @@ func pruneV2CheckpointRef(ctx context.Context, repo *git.Repository, v2Store *ch return nil } + authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, newRoot, parentHash, - fmt.Sprintf("Reset checkpoint before force migration: %s\n", cpID), - migrateAuthorName, migrateAuthorEmail) + checkpoint.MigrationCommitMessage(fmt.Sprintf("Reset checkpoint before force migration: %s", cpID)), + authorName, authorEmail) if err != nil { return fmt.Errorf("failed to create v2 prune commit for %s: %w", refName, err) } @@ -1284,9 +1286,10 @@ func pruneV2ArchivedCheckpointRef(ctx context.Context, repo *git.Repository, v2S return fmt.Errorf("failed to recompute generation metadata for %s: %w", refName, err) } + authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, newRoot, parentHash, - fmt.Sprintf("Reset checkpoint before force migration: %s\n", cpID), - migrateAuthorName, migrateAuthorEmail) + checkpoint.MigrationCommitMessage(fmt.Sprintf("Reset checkpoint before force migration: %s", cpID)), + authorName, authorEmail) if err != nil { return fmt.Errorf("failed to create v2 prune commit for %s: %w", refName, err) } @@ -1391,8 +1394,10 @@ func buildMigrateWriteOpts(content *checkpoint.SessionContent, info checkpoint.C TranscriptIdentifierAtStart: m.TranscriptIdentifierAtStart, IsTask: m.IsTask, ToolUseID: m.ToolUseID, - AuthorName: migrateAuthorName, - AuthorEmail: migrateAuthorEmail, + // AuthorName/AuthorEmail intentionally left zero: WriteCommittedMainBatch + // falls back to GetGitAuthorFromRepo so the commit is signable by the + // migrating user. Provenance is carried by the migration trailers in the + // batch commit message. } } diff --git a/cmd/entire/cli/strategy/generation_repair.go b/cmd/entire/cli/strategy/generation_repair.go index 311708422..937560978 100644 --- a/cmd/entire/cli/strategy/generation_repair.go +++ b/cmd/entire/cli/strategy/generation_repair.go @@ -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 { @@ -132,9 +127,10 @@ func repairOneV2GenerationMetadata( return false, nil } + authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) newCommitHash, commitErr := checkpoint.CreateCommit(ctx, repo, newTreeHash, oldCommitHash, - fmt.Sprintf("Repair generation metadata: %s\n", candidate.Name), - repairAuthorName, repairAuthorEmail) + checkpoint.MigrationCommitMessage(fmt.Sprintf("Repair generation metadata: %s", candidate.Name)), + authorName, authorEmail) if commitErr != nil { return false, fmt.Errorf("failed to create repair commit: %w", commitErr) } From 0361b99ebeb5db44a40d9022f94b200fc37a34e8 Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Thu, 14 May 2026 20:40:40 +0200 Subject: [PATCH 2/5] trailers: move migration trailer formatting into the trailers package The Co-Authored-By + Entire-Migration trailer block was defined inside cmd/entire/cli/checkpoint/committed.go alongside CreateCommit. That's the wrong package: trailers/trailers.go already owns every other Entire-* trailer key and Format helper, and the migration trailer fits the same pattern exactly. Relocates the two value constants and the formatter, keeping the existing naming conventions: MigrationTrailerKey = "Entire-Migration" MigrationCoAuthor = "Entire Migration " func FormatMigration(message string) string All seven call sites now call trailers.FormatMigration directly. No behavior change; the produced commit message is byte-identical to the prior MigrationCommitMessage output. Co-Authored-By: Claude Opus 4.7 (1M context) Entire-Checkpoint: 6b357863a3aa --- cmd/entire/cli/checkpoint/committed.go | 17 ----------------- cmd/entire/cli/checkpoint/v2_committed.go | 8 ++++---- cmd/entire/cli/migrate.go | 11 ++++++----- cmd/entire/cli/strategy/generation_repair.go | 3 ++- cmd/entire/cli/trailers/trailers.go | 18 ++++++++++++++++++ 5 files changed, 30 insertions(+), 27 deletions(-) diff --git a/cmd/entire/cli/checkpoint/committed.go b/cmd/entire/cli/checkpoint/committed.go index ae9bbc217..69a2f767e 100644 --- a/cmd/entire/cli/checkpoint/committed.go +++ b/cmd/entire/cli/checkpoint/committed.go @@ -1826,23 +1826,6 @@ func GetGitAuthorFromRepo(repo *git.Repository) (name, email string) { return name, email } -// MigrationCoAuthorTrailer marks a commit as produced by Entire's migration or -// repair tooling. The author/committer remain the real git user (so commit -// signing keeps working); this trailer carries the provenance signal. -const MigrationCoAuthorTrailer = "Co-Authored-By: Entire Migration " - -// MigrationProvenanceTrailer is a greppable boolean flag that distinguishes -// tool-produced commits from user work without parsing the co-author email. -const MigrationProvenanceTrailer = "Entire-Migration: true" - -// MigrationCommitMessage wraps subject with the standard migration trailers. -// All migrate.go and generation_repair.go commit sites use this so the -// provenance signal stays consistent across every tool-produced commit. -func MigrationCommitMessage(subject string) string { - subject = strings.TrimRight(subject, "\n") - return fmt.Sprintf("%s\n\n%s\n%s\n", subject, MigrationCoAuthorTrailer, MigrationProvenanceTrailer) -} - // 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) { diff --git a/cmd/entire/cli/checkpoint/v2_committed.go b/cmd/entire/cli/checkpoint/v2_committed.go index 00cf85359..e699ff9d0 100644 --- a/cmd/entire/cli/checkpoint/v2_committed.go +++ b/cmd/entire/cli/checkpoint/v2_committed.go @@ -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" @@ -129,10 +130,9 @@ func (s *V2GitStore) WriteCommittedMainBatch(ctx context.Context, batch []WriteC } // One commit, one ref update for the entire batch. WriteCommittedMainBatch - // is migration-only (see callers); the message carries the standard - // migration trailers so provenance is consistent across every migration - // commit, including this batched /main write. - commitMsg := MigrationCommitMessage(fmt.Sprintf("Migrate batch: %d checkpoint(s), %d session(s)", len(groupOrder), len(batch))) + // 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 == "" { diff --git a/cmd/entire/cli/migrate.go b/cmd/entire/cli/migrate.go index c3447f6ec..081d9cf82 100644 --- a/cmd/entire/cli/migrate.go +++ b/cmd/entire/cli/migrate.go @@ -24,6 +24,7 @@ import ( "github.com/entireio/cli/cmd/entire/cli/logging" "github.com/entireio/cli/cmd/entire/cli/paths" "github.com/entireio/cli/cmd/entire/cli/strategy" + "github.com/entireio/cli/cmd/entire/cli/trailers" "github.com/entireio/cli/cmd/entire/cli/transcript/compact" "github.com/entireio/cli/cmd/entire/cli/versioninfo" "github.com/entireio/cli/perf" @@ -881,7 +882,7 @@ func writeMigratedFinalFullCurrent(ctx context.Context, repo *git.Repository, v2 authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, treeHash, parentHash, - checkpoint.MigrationCommitMessage("Write migrated partial generation"), + trailers.FormatMigration("Write migrated partial generation"), authorName, authorEmail) if err != nil { return fmt.Errorf("create migrated full/current commit: %w", err) @@ -976,7 +977,7 @@ func writeMigratedFullGeneration(ctx context.Context, repo *git.Repository, v2St authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, treeHash, plumbing.ZeroHash, - checkpoint.MigrationCommitMessage(fmt.Sprintf("Archive migrated generation: %s", refName)), + trailers.FormatMigration(fmt.Sprintf("Archive migrated generation: %s", refName)), authorName, authorEmail) if err != nil { return plumbing.ZeroHash, fmt.Errorf("create migrated generation commit: %w", err) @@ -1146,7 +1147,7 @@ func ensureEmptyV2FullCurrent(ctx context.Context, repo *git.Repository) error { authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, emptyTreeHash, plumbing.ZeroHash, - checkpoint.MigrationCommitMessage("Start generation"), + trailers.FormatMigration("Start generation"), authorName, authorEmail) if err != nil { return fmt.Errorf("create empty v2 full/current commit: %w", err) @@ -1231,7 +1232,7 @@ func pruneV2CheckpointRef(ctx context.Context, repo *git.Repository, v2Store *ch authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, newRoot, parentHash, - checkpoint.MigrationCommitMessage(fmt.Sprintf("Reset checkpoint before force migration: %s", cpID)), + trailers.FormatMigration(fmt.Sprintf("Reset checkpoint before force migration: %s", cpID)), authorName, authorEmail) if err != nil { return fmt.Errorf("failed to create v2 prune commit for %s: %w", refName, err) @@ -1288,7 +1289,7 @@ func pruneV2ArchivedCheckpointRef(ctx context.Context, repo *git.Repository, v2S authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, newRoot, parentHash, - checkpoint.MigrationCommitMessage(fmt.Sprintf("Reset checkpoint before force migration: %s", cpID)), + trailers.FormatMigration(fmt.Sprintf("Reset checkpoint before force migration: %s", cpID)), authorName, authorEmail) if err != nil { return fmt.Errorf("failed to create v2 prune commit for %s: %w", refName, err) diff --git a/cmd/entire/cli/strategy/generation_repair.go b/cmd/entire/cli/strategy/generation_repair.go index 937560978..689da742a 100644 --- a/cmd/entire/cli/strategy/generation_repair.go +++ b/cmd/entire/cli/strategy/generation_repair.go @@ -8,6 +8,7 @@ import ( "github.com/entireio/cli/cmd/entire/cli/checkpoint" "github.com/entireio/cli/cmd/entire/cli/checkpoint/remote" "github.com/entireio/cli/cmd/entire/cli/paths" + "github.com/entireio/cli/cmd/entire/cli/trailers" "github.com/go-git/go-git/v6" "github.com/go-git/go-git/v6/plumbing" ) @@ -129,7 +130,7 @@ func repairOneV2GenerationMetadata( authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) newCommitHash, commitErr := checkpoint.CreateCommit(ctx, repo, newTreeHash, oldCommitHash, - checkpoint.MigrationCommitMessage(fmt.Sprintf("Repair generation metadata: %s", candidate.Name)), + trailers.FormatMigration(fmt.Sprintf("Repair generation metadata: %s", candidate.Name)), authorName, authorEmail) if commitErr != nil { return false, fmt.Errorf("failed to create repair commit: %w", commitErr) diff --git a/cmd/entire/cli/trailers/trailers.go b/cmd/entire/cli/trailers/trailers.go index a3c1b4403..ea6b5f590 100644 --- a/cmd/entire/cli/trailers/trailers.go +++ b/cmd/entire/cli/trailers/trailers.go @@ -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 " ) // Pre-compiled regexes for trailer parsing. @@ -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) +} + // 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 { From db0ad09746219adf33660783aa1d813f354b79fa Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Thu, 14 May 2026 20:43:28 +0200 Subject: [PATCH 3/5] checkpoint: collapse migration commit sites via CreateMigrationCommit Every migration/repair site followed the same four-line shape: authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) commitHash, err := checkpoint.CreateCommit(ctx, repo, tree, parent, trailers.FormatMigration("..."), authorName, authorEmail) That's six call sites repeating identical bookkeeping. Introduces checkpoint.CreateMigrationCommit(ctx, repo, tree, parent, subject) which wraps the author lookup + FormatMigration + CreateCommit chain so each site becomes a single expression. Five sites in migrate.go and one in generation_repair.go collapse to this helper; both files drop their direct trailers import as a result. The /main batch path in v2_committed.go keeps calling trailers.FormatMigration directly because it has its own ref-update flow that doesn't go through CreateCommit. Also tightens an over-long explanatory comment on buildMigrateWriteOpts to two lines. Co-Authored-By: Claude Opus 4.7 (1M context) Entire-Checkpoint: d3910f0d9222 --- cmd/entire/cli/checkpoint/committed.go | 9 +++++ cmd/entire/cli/migrate.go | 37 +++++++------------- cmd/entire/cli/strategy/generation_repair.go | 7 ++-- 3 files changed, 23 insertions(+), 30 deletions(-) diff --git a/cmd/entire/cli/checkpoint/committed.go b/cmd/entire/cli/checkpoint/committed.go index 69a2f767e..766ad45f4 100644 --- a/cmd/entire/cli/checkpoint/committed.go +++ b/cmd/entire/cli/checkpoint/committed.go @@ -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) { diff --git a/cmd/entire/cli/migrate.go b/cmd/entire/cli/migrate.go index 081d9cf82..37163789d 100644 --- a/cmd/entire/cli/migrate.go +++ b/cmd/entire/cli/migrate.go @@ -24,7 +24,6 @@ import ( "github.com/entireio/cli/cmd/entire/cli/logging" "github.com/entireio/cli/cmd/entire/cli/paths" "github.com/entireio/cli/cmd/entire/cli/strategy" - "github.com/entireio/cli/cmd/entire/cli/trailers" "github.com/entireio/cli/cmd/entire/cli/transcript/compact" "github.com/entireio/cli/cmd/entire/cli/versioninfo" "github.com/entireio/cli/perf" @@ -880,10 +879,8 @@ func writeMigratedFinalFullCurrent(ctx context.Context, repo *git.Repository, v2 return fmt.Errorf("build migrated full/current tree: %w", err) } - authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) - commitHash, err := checkpoint.CreateCommit(ctx, repo, treeHash, parentHash, - trailers.FormatMigration("Write migrated partial generation"), - authorName, authorEmail) + 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) } @@ -975,10 +972,8 @@ func writeMigratedFullGeneration(ctx context.Context, repo *git.Repository, v2St return plumbing.ZeroHash, fmt.Errorf("add generation metadata: %w", err) } - authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) - commitHash, err := checkpoint.CreateCommit(ctx, repo, treeHash, plumbing.ZeroHash, - trailers.FormatMigration(fmt.Sprintf("Archive migrated generation: %s", refName)), - authorName, authorEmail) + 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) } @@ -1145,10 +1140,8 @@ func ensureEmptyV2FullCurrent(ctx context.Context, repo *git.Repository) error { return fmt.Errorf("build empty v2 full/current tree: %w", err) } - authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) - commitHash, err := checkpoint.CreateCommit(ctx, repo, emptyTreeHash, plumbing.ZeroHash, - trailers.FormatMigration("Start generation"), - authorName, authorEmail) + 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) } @@ -1230,10 +1223,8 @@ func pruneV2CheckpointRef(ctx context.Context, repo *git.Repository, v2Store *ch return nil } - authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) - commitHash, err := checkpoint.CreateCommit(ctx, repo, newRoot, parentHash, - trailers.FormatMigration(fmt.Sprintf("Reset checkpoint before force migration: %s", cpID)), - authorName, authorEmail) + 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) } @@ -1287,10 +1278,8 @@ func pruneV2ArchivedCheckpointRef(ctx context.Context, repo *git.Repository, v2S return fmt.Errorf("failed to recompute generation metadata for %s: %w", refName, err) } - authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) - commitHash, err := checkpoint.CreateCommit(ctx, repo, newRoot, parentHash, - trailers.FormatMigration(fmt.Sprintf("Reset checkpoint before force migration: %s", cpID)), - authorName, authorEmail) + 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) } @@ -1395,10 +1384,8 @@ func buildMigrateWriteOpts(content *checkpoint.SessionContent, info checkpoint.C TranscriptIdentifierAtStart: m.TranscriptIdentifierAtStart, IsTask: m.IsTask, ToolUseID: m.ToolUseID, - // AuthorName/AuthorEmail intentionally left zero: WriteCommittedMainBatch - // falls back to GetGitAuthorFromRepo so the commit is signable by the - // migrating user. Provenance is carried by the migration trailers in the - // batch commit message. + // Author left zero so WriteCommittedMainBatch falls back to the real + // git user; provenance lives in the migration trailer block. } } diff --git a/cmd/entire/cli/strategy/generation_repair.go b/cmd/entire/cli/strategy/generation_repair.go index 689da742a..4db61445b 100644 --- a/cmd/entire/cli/strategy/generation_repair.go +++ b/cmd/entire/cli/strategy/generation_repair.go @@ -8,7 +8,6 @@ import ( "github.com/entireio/cli/cmd/entire/cli/checkpoint" "github.com/entireio/cli/cmd/entire/cli/checkpoint/remote" "github.com/entireio/cli/cmd/entire/cli/paths" - "github.com/entireio/cli/cmd/entire/cli/trailers" "github.com/go-git/go-git/v6" "github.com/go-git/go-git/v6/plumbing" ) @@ -128,10 +127,8 @@ func repairOneV2GenerationMetadata( return false, nil } - authorName, authorEmail := checkpoint.GetGitAuthorFromRepo(repo) - newCommitHash, commitErr := checkpoint.CreateCommit(ctx, repo, newTreeHash, oldCommitHash, - trailers.FormatMigration(fmt.Sprintf("Repair generation metadata: %s", candidate.Name)), - authorName, authorEmail) + newCommitHash, commitErr := checkpoint.CreateMigrationCommit(ctx, repo, newTreeHash, oldCommitHash, + fmt.Sprintf("Repair generation metadata: %s", candidate.Name)) if commitErr != nil { return false, fmt.Errorf("failed to create repair commit: %w", commitErr) } From a1e5e6977182deb56e44c7c3e324ea668e7ffb3d Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Thu, 14 May 2026 21:03:24 +0200 Subject: [PATCH 4/5] strategy: drop fmt.Sprintf single-substitution in repair commit subject golangci-lint's perfsprint rule flagged the formatted subject we pass to CreateMigrationCommit, since candidate.Name is already a string and the format string has only one verb. Switch to plain concatenation. Local 'mise run lint' had silently auto-fixed this with --fix; CI runs without --fix and surfaced the issue. Equivalent runtime output. Co-Authored-By: Claude Opus 4.7 (1M context) Entire-Checkpoint: 988b07367ccd --- cmd/entire/cli/strategy/generation_repair.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/entire/cli/strategy/generation_repair.go b/cmd/entire/cli/strategy/generation_repair.go index 4db61445b..349875f7f 100644 --- a/cmd/entire/cli/strategy/generation_repair.go +++ b/cmd/entire/cli/strategy/generation_repair.go @@ -128,7 +128,7 @@ func repairOneV2GenerationMetadata( } newCommitHash, commitErr := checkpoint.CreateMigrationCommit(ctx, repo, newTreeHash, oldCommitHash, - fmt.Sprintf("Repair generation metadata: %s", candidate.Name)) + "Repair generation metadata: "+candidate.Name) if commitErr != nil { return false, fmt.Errorf("failed to create repair commit: %w", commitErr) } From e24d354e2c46215ca5e92686e1c717484dcd6784 Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Thu, 14 May 2026 21:25:21 +0200 Subject: [PATCH 5/5] trailers: pin FormatMigration output with a unit test The migration trailer block (Co-Authored-By identity + Entire-Migration boolean) is the only signal distinguishing tool-produced commits from user work on the checkpoints/v2 refs. Add a focused test, matching the style of TestFormatMetadata, so accidental changes to the email, trailer ordering, or blank-line separation fail at unit-test time rather than landing silently in pushed commits. Co-Authored-By: Claude Opus 4.7 (1M context) Entire-Checkpoint: 90baba88311d --- cmd/entire/cli/trailers/trailers_test.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/cmd/entire/cli/trailers/trailers_test.go b/cmd/entire/cli/trailers/trailers_test.go index 2fd0e8961..be8752dd4 100644 --- a/cmd/entire/cli/trailers/trailers_test.go +++ b/cmd/entire/cli/trailers/trailers_test.go @@ -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 \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