Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions document-release/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,7 @@ committing.

```bash
git commit -m "$(cat <<'EOF'
docs: update project documentation for vX.Y.Z.W

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
docs: update project documentation for vX.Y.Z.W{{COMMIT_COAUTHOR_TRAILER_BLOCK}}
EOF
)"
```
Expand Down
11 changes: 11 additions & 0 deletions scripts/gen-skill-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ interface TemplateContext {
paths: HostPaths;
}

const HOST_COMMIT_COAUTHOR_TRAILERS: Record<Host, string> = {
claude: 'Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>',
codex: '',
};

// ─── Shared Design Constants ────────────────────────────────

/** gstack's 10 AI slop anti-patterns — shared between DESIGN_METHODOLOGY and DESIGN_HARD_RULES */
Expand Down Expand Up @@ -2795,9 +2800,15 @@ function generateSlugSetup(ctx: TemplateContext): string {
return `eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG`;
}

function generateCommitCoauthorTrailerBlock(ctx: TemplateContext): string {
const trailer = HOST_COMMIT_COAUTHOR_TRAILERS[ctx.host];
return trailer ? `\n\n${trailer}` : '';
}

const RESOLVERS: Record<string, (ctx: TemplateContext) => string> = {
SLUG_EVAL: generateSlugEval,
SLUG_SETUP: generateSlugSetup,
COMMIT_COAUTHOR_TRAILER_BLOCK: generateCommitCoauthorTrailerBlock,
COMMAND_REFERENCE: generateCommandReference,
SNAPSHOT_FLAGS: generateSnapshotFlags,
PREAMBLE: generatePreamble,
Expand Down
2 changes: 1 addition & 1 deletion ship/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -1388,7 +1388,7 @@ Save this summary — it goes into the PR body in Step 8.
5. Compose each commit message:
- First line: `<type>: <summary>` (type = feat/fix/chore/refactor/docs)
- Body: brief description of what this commit contains
- Only the **final commit** (VERSION + CHANGELOG) gets the version tag and co-author trailer:
- Only the **final commit** (VERSION + CHANGELOG) gets the version tag. If the current host uses a commit footer, include it only on that final commit:

```bash
git commit -m "$(cat <<'EOF'
Expand Down
6 changes: 2 additions & 4 deletions ship/SKILL.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -399,13 +399,11 @@ Save this summary — it goes into the PR body in Step 8.
5. Compose each commit message:
- First line: `<type>: <summary>` (type = feat/fix/chore/refactor/docs)
- Body: brief description of what this commit contains
- Only the **final commit** (VERSION + CHANGELOG) gets the version tag and co-author trailer:
- Only the **final commit** (VERSION + CHANGELOG) gets the version tag. If the current host uses a commit footer, include it only on that final commit:

```bash
git commit -m "$(cat <<'EOF'
chore: bump version and changelog (vX.Y.Z.W)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
chore: bump version and changelog (vX.Y.Z.W){{COMMIT_COAUTHOR_TRAILER_BLOCK}}
EOF
)"
```
Expand Down
26 changes: 26 additions & 0 deletions test/gen-skill-docs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ describe('gen-skill-docs', () => {
expect(browseTmpl).toContain('{{PREAMBLE}}');
});

test('ship and document-release templates use host-aware commit trailer placeholder', () => {
const shipTmpl = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md.tmpl'), 'utf-8');
const documentReleaseTmpl = fs.readFileSync(path.join(ROOT, 'document-release', 'SKILL.md.tmpl'), 'utf-8');

expect(shipTmpl).toContain('{{COMMIT_COAUTHOR_TRAILER_BLOCK}}');
expect(documentReleaseTmpl).toContain('{{COMMIT_COAUTHOR_TRAILER_BLOCK}}');
});

test('generated SKILL.md contains contributor mode check', () => {
const content = fs.readFileSync(path.join(ROOT, 'SKILL.md'), 'utf-8');
expect(content).toContain('Contributor Mode');
Expand Down Expand Up @@ -1168,6 +1176,24 @@ describe('Codex generation (--host codex)', () => {
const codexContent = fs.readFileSync(path.join(AGENTS_DIR, 'gstack-ship', 'SKILL.md'), 'utf-8');
expect(codexContent).not.toContain('Codex design voice');
});

test('codex host omits AI co-author trailers from ship and document-release commit examples', () => {
const shipContent = fs.readFileSync(path.join(AGENTS_DIR, 'gstack-ship', 'SKILL.md'), 'utf-8');
const documentReleaseContent = fs.readFileSync(path.join(AGENTS_DIR, 'gstack-document-release', 'SKILL.md'), 'utf-8');

expect(shipContent).not.toContain('Co-Authored-By:');
expect(documentReleaseContent).not.toContain('Co-Authored-By:');
expect(shipContent).not.toContain('noreply@anthropic.com');
expect(documentReleaseContent).not.toContain('noreply@anthropic.com');
});

test('Claude output preserves Claude co-author trailers in ship and document-release', () => {
const shipContent = fs.readFileSync(path.join(ROOT, 'ship', 'SKILL.md'), 'utf-8');
const documentReleaseContent = fs.readFileSync(path.join(ROOT, 'document-release', 'SKILL.md'), 'utf-8');

expect(shipContent).toContain('Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>');
expect(documentReleaseContent).toContain('Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>');
});
});

// ─── Setup script validation ─────────────────────────────────
Expand Down