Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ refactor: simplify DAG executor
test: add unit tests for cost router
```

Do not add `Co-Authored-By` or any attribution trailer to commits or PRs unless a maintainer explicitly asks for it. Do not infer authorship from the model you are using.

### Code Style

- TypeScript strict mode — `tsconfig.json` sets `"strict": true`
Expand Down
4 changes: 4 additions & 0 deletions assets/agents/nexus-architect.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ You are a senior software architect. You design systems that are scalable, resil
- Synchronous chains that create tight coupling
- Over-engineering simple problems (YAGNI)

## Commits

- NEVER add `Co-Authored-By` or any attribution trailer (e.g. `Co-Authored-By: Claude`) to a commit or PR unless the user explicitly asks for it in this conversation. Do not infer authorship from the model in use.

## Output Contract

Return a written architecture direction, not code. It must contain:
Expand Down
4 changes: 4 additions & 0 deletions assets/agents/nexus-coder.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ You are a senior software engineer who writes clean, maintainable, production-re
- Tests covering happy path, edge cases, and error paths
- Brief inline comments for complex logic (why, not what)

## Commits

- NEVER add `Co-Authored-By` or any attribution trailer (e.g. `Co-Authored-By: Claude`) to a commit or PR unless the user explicitly asks for it in this conversation. Do not infer authorship from the model in use.

## Output Contract

Return what you changed and how it was verified. It must contain:
Expand Down
4 changes: 4 additions & 0 deletions assets/agents/nexus-documenter.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ You are a technical writer who creates documentation that developers actually wa
- TODO/FIXME/HACK comments are tracked and explained
- Changelog follows semantic versioning with clear descriptions

## Commits

- NEVER add `Co-Authored-By` or any attribution trailer (e.g. `Co-Authored-By: Claude`) to a commit or PR unless the user explicitly asks for it in this conversation. Do not infer authorship from the model in use.

## Output Contract

Documentation that is true of the code as it is now. It must contain:
Expand Down
4 changes: 4 additions & 0 deletions assets/agents/nexus-explorer.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ You are a code archaeologist. You navigate unknown codebases efficiently and bui
- Report uncertainty explicitly — don't guess about unexamined code
- Cite specific file paths and line numbers for all findings

## Commits

- NEVER add `Co-Authored-By` or any attribution trailer (e.g. `Co-Authored-By: Claude`) to a commit or PR unless the user explicitly asks for it in this conversation. Do not infer authorship from the model in use.

## Output Contract

A map, with evidence. It must contain:
Expand Down
1 change: 1 addition & 0 deletions assets/agents/nexus-orchestrator.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ When code changes are needed, follow this workflow:
- One logical change per commit
- Imperative mood in commit message
- Reference issues if applicable
- NEVER add `Co-Authored-By` or any attribution trailer (e.g. `Co-Authored-By: Claude`) to commits or PR descriptions unless the user explicitly asks for it in this conversation. Do not infer authorship from the model in use.

### 4. Pull Request
- Create PR with descriptive title and body
Expand Down
4 changes: 4 additions & 0 deletions assets/agents/nexus-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ For each finding:
- Be thorough — check for issues the author might have missed
- Prioritize — Critical/High issues first, then Medium/Low

## Commits

- NEVER add `Co-Authored-By` or any attribution trailer (e.g. `Co-Authored-By: Claude`) to a commit or PR unless the user explicitly asks for it in this conversation. Do not infer authorship from the model in use.

## Output Contract

Findings, ordered by severity, and nothing else. Each finding must carry:
Expand Down
4 changes: 4 additions & 0 deletions assets/agents/nexus-tester.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ You are a QA engineer who writes tests that catch real bugs, not just increase c
- Edge case coverage alongside happy path
- Mock/stub strategy that doesn't hide real bugs

## Commits

- NEVER add `Co-Authored-By` or any attribution trailer (e.g. `Co-Authored-By: Claude`) to a commit or PR unless the user explicitly asks for it in this conversation. Do not infer authorship from the model in use.

## Output Contract

Tests plus evidence they run. It must contain:
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,7 @@ export function buildGitFlowConventionSection(

lines.push(
'- **Never commit, push, or merge on your own initiative.** Report the change you made and let the user decide. If the user explicitly asks you to commit, push or merge, that is the one case where you do.',
'- **No attribution trailers.** Never add `Co-Authored-By` or any similar trailer (e.g. `Co-Authored-By: Claude`) to a commit or PR unless the user explicitly asks for it in this conversation. Do not infer authorship from the model in use.',
'',
'You can check where things stand with `nexus.git.check`. It only reports — it never changes anything and never refuses.',
'',
Expand Down
42 changes: 41 additions & 1 deletion test/agent-definitions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,44 @@ describe('each role states an output contract and when to escalate', () => {
expect(content).toMatch(/^## When to Escalate$/m)
}
)
})
})

describe('commit-capable prompts prohibit attribution trailers', () => {
// Roles whose frontmatter grants `shell` can run `git commit`; the
// orchestrator opens PRs. Models hallucinate trailers such as
// "Co-Authored-By: Claude" even when the model is not Claude, so the rule is
// pinned per prompt. Only designer denies `shell`; reviewer and explorer
// deny only `edit`, and OpenCode's permission resolution falls through to a
// catch-all allow for them — they CAN run `git commit`, so they carry the
// rule too. Designer's protection is the appended git-convention section
// (`buildGitFlowConventionSection`), covered in `test/git-flow.test.ts`.
const COMMIT_CAPABLE = ['nexus-coder.md', 'nexus-tester.md', 'nexus-documenter.md', 'nexus-architect.md', 'nexus-reviewer.md', 'nexus-explorer.md', ORCHESTRATOR]

it.each(COMMIT_CAPABLE)(
'%s forbids Co-Authored-By and similar trailers unless the user asks',
(name) => {
// One anchored regex, not three loose substrings: the substring version
// passes for an INVERTED rule ("never add ... unless ... Do not
// infer ..."), because each fragment is still present somewhere in the
// file. The anchor ties the fragments together in the order the rule
// states them, so an inversion fails here.
const content = readAgent(name)
expect(content).toMatch(
/NEVER add `Co-Authored-By`[\s\S]{0,120}unless the user explicitly asks for it in this conversation\. Do not infer authorship from the model in use\./
)
}
)

it('every agent that can shell carries the attribution rule', () => {
// OpenCode's permission resolution falls through to a catch-all allow, so
// an agent that never names `shell` can still run `git commit`. Stated
// that way round on purpose: unless the frontmatter explicitly denies
// `shell`, the prompt must carry the rule. This is the invariant that
// would have caught reviewer and explorer missing it.
for (const name of [...SPAWNABLE_ROLES.map((r) => `nexus-${r}.md`), ORCHESTRATOR]) {
const frontmatter = frontmatterOf(readAgent(name))
const shellDenied = /action:\s*shell[\s\S]*?effect:\s*deny/.test(frontmatter)
if (!shellDenied) expect(readAgent(name)).toMatch(/Co-Authored-By/)
}
})
})
26 changes: 26 additions & 0 deletions test/git-flow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,32 @@ describe('the agent-markdown convention', () => {
expect(section).toContain('Never commit, push, or merge on your own initiative')
})

it('never emits an attribution-trailer clause with any sub-key off', () => {
// The no-Co-Authored-By rule is unconditional for the same reason the
// unprompted-commit rule is: models hallucinate trailers such as
// "Co-Authored-By: Claude" even when the model is not Claude, and no
// gitFlow key is a knob for authorship. Pinned so a refactor that drops it
// behind a key fails here rather than in a polluted commit history.
//
// `gitFlow.enabled: false` is deliberately NOT one of the configs below:
// that is the master switch, and it removes the whole convention section by
// design — `src/index.ts` gates `buildGitFlowConventionSection` on
// `gitFlowResolved.active`. What this pins is that no SUB-key can disable
// the clause.
for (const config of [
ALL_ON,
{ ...ALL_ON, requireBranch: false },
{ ...ALL_ON, conventionalCommits: false },
{ ...ALL_ON, prBeforeMerge: false },
{ ...ALL_ON, requireBranch: false, conventionalCommits: false, prBeforeMerge: false },
]) {
const section = buildGitFlowConventionSection(config, { isRepo: true, repoRoot: '/x', repoId: '/x/.git', branch: 'feat/y', detached: false, hasRemote: true, remote: 'origin', known: true })
expect(section).toContain('No attribution trailers')
expect(section).toContain('Co-Authored-By')
expect(section).toContain('unless the user explicitly asks')
}
})

it('tells the agent the section can be turned off, and names the check as read-only', () => {
const section = buildGitFlowConventionSection(ALL_ON, { isRepo: true, repoRoot: '/x', repoId: '/x/.git', branch: 'main', detached: false, hasRemote: false, remote: null, known: true })
expect(section).toContain('gitFlow.enabled')
Expand Down
Loading