Auto-merge did nothing on a fresh container: tasks reached in-review, the merge began, and FN-001 sat at status:merging forever with no error in the UI. The cause was that git could not commit at all — `git commit` in the container dies with "Author identity unknown ... Please tell me who you are", because a container has no git identity and Fusion was borrowing the environment's. Only workspace-fence-ref.ts ever passed an explicit identity. The merge commits, the merger's --amend, and the experiment git-ops all inherited whatever user.name/user.email the host happened to have. The existing commitAuthor* settings only added a Co-authored-by TRAILER; they never set the author. resolveCommitIdentity (packages/engine/src/git-identity.ts) now resolves: operator commitAuthor* settings > the acting agent > Fusion. An agent-derived identity is `<Agent Name> (Fusion) <slug@agents.fusion.local>`, so history attributes a change to the agent that made it instead of one anonymous bot, per the operator's request. Applied at mergerCommitEnv — the single env all eight merger commit sites share — and via `-c user.name/-c user.email` for merger-ai and experiment git-ops, which build their own argv and bypass that env. Author AND committer are pinned: git fails on a missing committer just as hard as on a missing author. `commitAuthorEnabled: false` returns undefined and restores ambient git config, so an operator who wants commits authored as themselves keeps that. Verified: 9 new helper tests, 95 engine auth+identity tests, pnpm test:gate green. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
68 lines
2.8 KiB
TypeScript
68 lines
2.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
commitIdentityArgs,
|
|
commitIdentityEnv,
|
|
FUSION_FALLBACK_IDENTITY,
|
|
resolveCommitIdentity,
|
|
} from "../git-identity.js";
|
|
|
|
/*
|
|
FNXC:GitIdentity 2026-08-18-07:55:
|
|
Fusion-authored commits must carry an identity Fusion chose. Relying on ambient `user.name`/
|
|
`user.email` meant that on a host with none — container, CI, fresh machine — git refused with
|
|
"Author identity unknown" and an auto-merge stalled at `status:merging` with nothing surfaced.
|
|
*/
|
|
describe("resolveCommitIdentity", () => {
|
|
it("attributes a commit to the acting agent", () => {
|
|
const identity = resolveCommitIdentity({ agent: { name: "QA Engineer", id: "agent_1" } });
|
|
expect(identity).toEqual({ name: "QA Engineer (Fusion)", email: "qa-engineer@agents.fusion.local" });
|
|
});
|
|
|
|
it("falls back to the agent id when it has no name", () => {
|
|
expect(resolveCommitIdentity({ agent: { id: "agent_42" } })?.email).toBe("agent-42@agents.fusion.local");
|
|
});
|
|
|
|
it("prefers explicit operator settings over the agent", () => {
|
|
const identity = resolveCommitIdentity({
|
|
agent: { name: "QA Engineer" },
|
|
settings: { commitAuthorName: "Release Bot", commitAuthorEmail: "release@example.com" },
|
|
});
|
|
expect(identity).toEqual({ name: "Release Bot", email: "release@example.com" });
|
|
});
|
|
|
|
it("still yields an identity when no agent is known", () => {
|
|
// The important half: never return undefined just because the caller lacks agent context.
|
|
expect(resolveCommitIdentity()).toEqual(FUSION_FALLBACK_IDENTITY);
|
|
});
|
|
|
|
it("opts out entirely when the operator disables Fusion authorship", () => {
|
|
// undefined means "leave git alone" — the escape hatch for commits authored as the operator.
|
|
expect(resolveCommitIdentity({ settings: { commitAuthorEnabled: false }, agent: { name: "QA" } })).toBeUndefined();
|
|
});
|
|
|
|
it("does not emit an unusable email for a name with no alphanumerics", () => {
|
|
expect(resolveCommitIdentity({ agent: { name: "***" } })).toEqual(FUSION_FALLBACK_IDENTITY);
|
|
});
|
|
});
|
|
|
|
describe("commit identity plumbing", () => {
|
|
it("pins committer as well as author", () => {
|
|
// Author alone is not enough: git fails on a missing COMMITTER identity just as hard.
|
|
expect(commitIdentityEnv({ name: "N", email: "e@x" })).toEqual({
|
|
GIT_AUTHOR_NAME: "N",
|
|
GIT_AUTHOR_EMAIL: "e@x",
|
|
GIT_COMMITTER_NAME: "N",
|
|
GIT_COMMITTER_EMAIL: "e@x",
|
|
});
|
|
});
|
|
|
|
it("emits nothing when authorship is opted out", () => {
|
|
expect(commitIdentityEnv(undefined)).toEqual({});
|
|
expect(commitIdentityArgs(undefined)).toEqual([]);
|
|
});
|
|
|
|
it("builds -c overrides for argv callers", () => {
|
|
expect(commitIdentityArgs({ name: "N", email: "e@x" })).toEqual(["-c", "user.name=N", "-c", "user.email=e@x"]);
|
|
});
|
|
});
|