Skip to content

appendCommitCoAuthorNote

appendCommitCoAuthorNote(systemPrompt, coAuthor?, modelIdentity?, filesystem?): string

Defined in: _worktrees/docs-release/gaunt-sloth/packages/core/src/utils/systemPromptNotes.ts:165

GS2-35/EXT-83: append the commit-writing rules to the composed code-mode system prompt.

Gaunt Sloth has no dedicated git-commit tool — the agent commits by calling run_shell_command with git commit, composing the message (including any trailer) itself. That leaves three things it must be told, and all three live here because all three are about committing:

  1. WHO the co-author is. Left unguided, models emit their own model name from trained habit, which is factually wrong: the commit was produced by Gaunt Sloth, not by the model. The note states the exact trailer to emit. EXT-83 — rather than enumerate model names not to write (a denylist is stale the day a new vendor ships, and an enumeration beside a catch-all teaches the model that the list is the rule), the correct name is SUPPLIED: the resolved ResolvedModelIdentity decorates the DEFAULT name as Gaunt Sloth (provider:model), so the real model is named while the authorship stays Gaunt Sloth’s.
  2. HOW the message reaches git. A commit message passed inline in a double-quoted shell argument is EXPANDED BY THE SHELL before git runs, so a message that quotes code the way ordinary technical prose does is executed as a command. The note states that mechanism rather than merely forbidding the construct — naming a construct without its mechanism has been measured not to work. A file path carries no shell metacharacters, so the file form removes the failure mode instead of asking the model to avoid it.
  3. HOW the change is staged. Rule 2 leaves the message file untracked in the working tree at git add time, so an unscoped git add -A / git add . / git commit -a sweeps it into the commit and onto the pull request. The note states that mechanism, steers staging to named paths, and allows the last resort for a change too large to enumerate only once the message file is out of the tree — an escape hatch that kept the file would reintroduce exactly the defect the rule closes. It binds at git add time, MID-flow while the model is still acting, because the tidy-up step at the end is the one that gets dropped. The scratchpad sentence beside it names NO path: the write tool refuses any path outside the working folder at every approval mode, so naming one buys a refused call. EXT-97 is the interim mitigation; the mechanism that owns the file end to end is EXT-93.

The clause that names the writing tool is gated on filesystem (EXT-84), through the one shared derivation isWriteFileToolRegistered — the same interpretation that decides which tools are actually registered. Naming an unregistered tool while forbidding both the shell fallback and the inline flag leaves the model NO compliant path, and its likeliest recovery is the inline form this note exists to prevent. So when the write tool is registered the note names it literally (the overwhelmingly common case, and a literal name is what makes the instruction actionable); when it is not, the note names no tool, keeps both prohibitions and the file form, and supplies the compliant path that remains: do not commit, and hand the message back to the user. That branch states no availability claim of its own — registering filesystem tools is the agent’s decision, made from the same filesystem value, so a note asserting “you have no file-writing tool” could be flatly false.

The note’s prose carries no backtick and no other markup — including no angle-bracket placeholder: it is the one piece of guidance whose subject is how to write a commit message, so quoting its own examples in backticks would demonstrate the exact style rule 2 exists to stop, and an angle-bracket placeholder copied literally is itself a shell input redirect. The <email> of the trailer line is the exception the RFC form requires, and is scoped out of the scan.

The identity is config-driven (commit.coAuthor in import(‘#src/config/types.js’).GthConfig). Each field falls back INDEPENDENTLY to the Gaunt Sloth account (DEFAULT_COMMIT_CO_AUTHOR_NAME / DEFAULT_COMMIT_CO_AUTHOR_EMAIL) — so a partial override (name only, or a config that bypassed the loader) still yields a complete trailer, and a fully-absent config yields the default account. Blank/whitespace values are treated as unset. An EXPLICITLY CONFIGURED name is emitted verbatim, with no identity spliced in — the user asked for that string; the identity decorates only the default. An unresolvable identity (undefined) falls back to the plain default name, never to a placeholder.

Backend-agnostic: composed through the shared code path, so any backend injects it (the git-commit capability rides on run_shell_command, exposed in code mode). Returns the note alone when there is no base prompt.

string | undefined

CommitCoAuthor

ResolvedModelIdentity

FilesystemToolsConfig

string