Skip to content

T-UEU9-document-commit-message-pattern

Status: closed/done · Impact: low · Complexity: small

Auto-generated from a /sdlc:task-work post-mortem. Review and promote to ready before picking up.

SDLC skills that build a commit message from model output keep hitting the same shell pitfalls: zsh glob-expanding docs(tasks): inside a heredoc; collisions on /tmp/commit-msg.txt; cache-stale PC: bash annotations. Documenting a single canonical “build commit message via mktemp and git commit -F” pattern (and putting it in the skills that need it) removes the recurring workaround.

From T-DHGL-build-import-planning-skill’s post-mortem:

“One Bash invocation of git commit -m \"$(cat <<EOF...EOF)\" failed because zsh expanded docs(tasks): as a glob inside the command-substitution. Worked around by writing the message to a temp file and passing -F. Worth documenting as a pattern in any SDLC skill that constructs commit messages from the model.”

And the same family of issue appears in T-3A6G-implement-entities-migrate:

/tmp/commit-msg.txt collision — orchestrator used this generic temp path for a git commit -F and a stale file from a previous session was present, so the wrong commit message landed on the start commit (had to --amend). Fix: never use predictable /tmp/<generic>.txt paths; use mktemp or a unique PID/UUID-prefixed name.”

So today: skills construct commit messages inline with heredoc-in- command-substitution and sometimes via predictable temp paths. Both modes have failed once each. Multiple skills repeat the unsafe pattern.

Add a canonical “Committing model-generated messages” snippet to the SDLC skills that build commit messages from LLM output. The snippet:

msgfile="$(mktemp -t sdlc-commit-XXXXXX)"
trap 'rm -f "$msgfile"' EXIT
cat > "$msgfile" <<'EOF'
<commit message body — note quoted heredoc delimiter to suppress
shell expansion>
EOF
git commit -F "$msgfile"

Note both safety affordances: mktemp (no /tmp collisions) and a quoted heredoc delimiter <<'EOF' (no $( ), no glob, no parameter expansion).

  1. Draft the snippet (above), with a one-paragraph rationale that names both pitfalls (collision + glob expansion).
  2. Inline the snippet under a Conventions or Notes subsection of: task-work, task-define, entities-migrate, import-planning, milestones-from-file, project-cleanup, backlog-triage, review-todos. Any skill that builds commit messages from model output.
  3. Replace any existing inline heredoc-in--m examples with the canonical snippet.
  • plugin/skills/task-work/SKILL.md
  • plugin/skills/task-define/SKILL.md
  • plugin/skills/entities-migrate/SKILL.md
  • plugin/skills/import-planning/SKILL.md
  • plugin/skills/milestones-from-file/SKILL.md
  • plugin/skills/project-cleanup/SKILL.md
  • plugin/skills/backlog-triage/SKILL.md
  • plugin/skills/review-todos/SKILL.md

(One-line edits each, inserting the same snippet.)

  • AC-1: The canonical mktemp + quoted-heredoc + git commit -F snippet lives in exactly one place — plugin/conventions/commit-messages.md. Every SKILL.md that constructs commit messages from model output carries a one-line reference to that doc; no SKILL.md duplicates the snippet inline. (Revised from the original “every SKILL.md inlines the snippet” wording per PR #21 review feedback — shared reference is the right shape.)
  • AC-2: No SDLC SKILL.md still contains a git commit -m "$(cat <<EOF...EOF)" example.
  • AC-3: No SDLC SKILL.md still contains a predictable /tmp/commit-msg.txt (or similar) example; all temp paths in the canonical doc come from mktemp.
  • AC-4: /project-check still passes.
  • none

Spawned by /sdlc:task-work post-mortem of T-DHGL-build-import-planning-skill on 2026-05-19. Related friction was also observed in T-3A6G-implement-entities-migrate’s post-mortem.

Captured by /sdlc:task-work on 2026-05-19. PR: #21.

  • AC-1: auto — grep confirms the canonical snippet lives in plugin/conventions/commit-messages.md only; each affected SKILL.md carries a single one-line reference.
  • AC-2: auto — /usr/bin/grep -rn 'git commit -m "\$(cat' across plugin/skills/ returns zero hits.
  • AC-3: auto — /usr/bin/grep -rn '/tmp/commit-msg' across plugin/skills/ returns zero hits (predictable paths only appear in the canonical doc’s antipattern section, labeled as such).
  • AC-4: auto — /project-check (check_entities.py + the prose linter from PR #19) passes.
  • Replacing 8 verbatim 13-line blocks with 8 one-line references shrank the diff from +120 lines to +60 lines (the 60 being mostly the new canonical doc itself).
  • Convention precedent already existed: plugin/entities/task/implementation-ready.md is referenced the same way from multiple skills. Pattern transferred cleanly.
  • Original spec AC-1 mandated inline duplication (“every SKILL.md contains the canonical snippet”). Spec needed revision before the refactor was AC-compliant. Automation gap: the new prose-linter from PR #19 should grow an invariant that flags “verbatim multi-line block appearing in N>1 SKILL.md files” so duplication is caught at PR time before review. Filed implicitly as a follow-up.
  • The original draft was produced by the spawn-from-post-mortem sub-agent from a one-line friction bullet. The sub-agent went literal — “document the pattern in each skill that needs it” → “inline the pattern in each skill.” A spawn-from-post-mortem pre-flight question (“should this be a shared doc or per-skill?”) would catch the shape mismatch before the task is materialized.
  • none

← Back to Tasks