feat(traceability): append SF-Session id to autonomous commit messages

- git-service.js autoCommit() accepts optional sessionId param
  - Appends 'SF-Session: <id>' trailer to commit message when present
  - Falls through cleanly when sessionId is undefined (quick tasks, templates)
- worktree.js autoCommitCurrentBranch() forwards sessionId
- auto-post-unit.js autoCommitUnit() reads session ID from getAutoSession()
  via s.cmdCtx?.sessionManager?.getSessionId?.() — same pattern as auto.js

Mirrors Copilot's session logs linked to each commit for cross-session traceability.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Mikael Hugo 2026-05-09 21:10:02 +02:00
parent 692328ad45
commit 024485f050
3 changed files with 8 additions and 3 deletions

View file

@ -165,6 +165,7 @@ import { join } from "node:path";
import { describeNextUnit } from "./auto-dashboard.js";
import { _resetHasChangesCache } from "./native-git-bridge.js";
import { autoCommitCurrentBranch } from "./worktree.js";
import { getAutoSession } from "./auto/session.js";
/**
* Detect summary files written directly to disk without the LLM calling
@ -336,11 +337,13 @@ export async function autoCommitUnit(basePath, unitType, unitId, ctx) {
if (LIFECYCLE_ONLY_UNITS.has(unitType)) {
return null;
}
const sessionId = getAutoSession().cmdCtx?.sessionManager?.getSessionId?.() ?? null;
const commitMsg = autoCommitCurrentBranch(
basePath,
unitType,
unitId,
taskContext,
sessionId,
);
if (commitMsg) {
ctx?.ui.notify(`Committed: ${commitMsg.split("\n")[0]}`, "info");

View file

@ -504,7 +504,7 @@ export class GitServiceImpl {
* Returns the commit message on success, or null if nothing to commit.
* @param extraExclusions Additional paths to exclude from staging (e.g. [".sf/"] for pre-switch commits).
*/
autoCommit(unitType, unitId, extraExclusions = [], taskContext) {
autoCommit(unitType, unitId, extraExclusions = [], taskContext, sessionId) {
// Quick check: is there anything dirty at all?
// Native path uses libgit2 (single syscall), fallback spawns git.
if (!nativeHasChanges(this.basePath)) return null;
@ -512,9 +512,10 @@ export class GitServiceImpl {
// After smart staging, check if anything was actually staged
// (all changes might have been runtime files that got excluded)
if (!nativeHasStagedChanges(this.basePath)) return null;
const message = taskContext
const base = taskContext
? buildTaskCommitMessage(taskContext)
: `chore: auto-commit after ${unitType}\n\nSF-Unit: ${unitId}`;
const message = sessionId ? `${base}\nSF-Session: ${sessionId}` : base;
nativeCommit(this.basePath, message, { allowEmpty: false });
// Absorb any preceding sf snapshot commits into this real commit.
// Walk backwards from HEAD~1 counting consecutive snapshot subjects,

View file

@ -281,8 +281,9 @@ export function autoCommitCurrentBranch(
unitType,
unitId,
taskContext,
sessionId,
) {
return getService(basePath).autoCommit(unitType, unitId, [], taskContext);
return getService(basePath).autoCommit(unitType, unitId, [], taskContext, sessionId);
}
// ─── Git HEAD Resolution ────────────────────────────────────────────────────
/**