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
1 change: 1 addition & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"includeCoAuthoredBy":false,"attribution":{"co_authored_by":false,"commit_message_footer":false}}
63 changes: 0 additions & 63 deletions .github/scripts/check-attribution.sh

This file was deleted.

65 changes: 65 additions & 0 deletions .github/scripts/check-dco.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/usr/bin/env bash
# check-dco.sh — require a DCO sign-off on every commit in a range.
#
# Every non-merge commit must contain a "Signed-off-by: Name <email>" line
# whose email matches the commit AUTHOR email. Merge commits are exempt
# (the repo is squash-merge only, so there should be none anyway).
#
# Usage:
# .github/scripts/check-dco.sh <base>..<head>
# .github/scripts/check-dco.sh <sha> # single commit and ancestors
#
# tests: exercise locally on a scratch branch before trusting it in CI —
# git checkout -b scratch/dco
# git commit --allow-empty -m "test: signed" -s
# git commit --allow-empty -m "test: unsigned"
# .github/scripts/check-dco.sh main..HEAD # must fail on the 2nd commit
# git checkout - && git branch -D scratch/dco
set -euo pipefail

range="${1:?usage: check-dco.sh <range>}"

fail=0
count=0
while IFS= read -r sha; do
count=$((count + 1))
author_email="$(git log -1 --format='%ae' "$sha")"
committer_email="$(git log -1 --format='%ce' "$sha")"

# GitHub web-flow commits (squash/merge performed by github.com itself,
# committer noreply@github.com) rewrite the author email to the merging
# account's GitHub address, so an exact sign-off==author match is
# impossible by construction. The underlying PR commits were already
# DCO-checked by this workflow's required pull_request run; for the
# resulting merge commit we require a sign-off to be present but skip
# the email match.
if [ "$committer_email" = "noreply@github.com" ]; then
if ! git log -1 --format='%B' "$sha" | grep -qi '^signed-off-by:'; then
echo "::error::Merge/squash commit ${sha} carries no Signed-off-by at all."
echo " subject: $(git log -1 --format='%s' "$sha")"
fail=1
fi
continue
fi

# The sign-off may sit anywhere in the message body: after a squash merge,
# GitHub concatenates commit messages, which moves trailers out of the
# strict trailer block. Match any "Signed-off-by:" line instead.
if ! git log -1 --format='%B' "$sha" \
| grep -i '^signed-off-by:' \
| grep -qF "<${author_email}>"; then
echo "::error::Commit ${sha} has no Signed-off-by matching its author <${author_email}>."
echo " subject: $(git log -1 --format='%s' "$sha")"
echo " fix: 'git commit --amend -s' for the last commit, or" \
"'git rebase --signoff' for a branch, then force-push."
fail=1
fi
done < <(git rev-list --no-merges "$range")

echo "check-dco: inspected ${count} commit(s) in ${range}"
if [ "$fail" -ne 0 ]; then
echo "::error::DCO check failed. Every commit must be signed off (git commit -s)." \
"See CONTRIBUTING.md §5 and https://developercertificate.org"
exit 1
fi
echo "check-dco: OK"
86 changes: 86 additions & 0 deletions .github/scripts/check-no-ai-attribution.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#!/usr/bin/env bash
# check-no-ai-attribution.sh — reject AI/bot attribution in commits.
#
# Policy (one line): AI assistance is welcome; AI attribution is not. Remove
# the trailer and recommit — you are the author of record.
#
# For every commit in the range this fails on any of:
# * message (subject + body + trailers, case-insensitive):
# - Co-authored-by trailers naming an AI/agent/bot vendor
# - "Generated with/by" watermarks
# - the robot emoji watermark
# - noreply.anthropic.com anywhere
# * author/committer identity:
# - *[bot]@users.noreply.github.com, *@noreply.anthropic.com,
# actions@github.com emails
# - names containing claude/copilot/devin/aider/codex/gemini
#
# Usage:
# .github/scripts/check-no-ai-attribution.sh <base>..<head>
#
# tests: exercise locally on a scratch branch before trusting it in CI —
# git checkout -b scratch/attribution
# git commit --allow-empty -s -m "test: clean commit"
# git commit --allow-empty -s -m "test: bad commit" \
# -m "Co-Authored-By: Example Bot <example[bot]@users.noreply.github.com>"
# .github/scripts/check-no-ai-attribution.sh main..HEAD # must fail once
# git checkout - && git branch -D scratch/attribution
set -euo pipefail

range="${1:?usage: check-no-ai-attribution.sh <range>}"

POLICY="AI assistance is welcome; AI attribution is not. Remove the trailer and recommit — you are the author of record."

# Message patterns, matched case-insensitively (ERE).
msg_patterns=(
'co-authored-by:[[:space:]]*.*\b(claude|anthropic|copilot|chatgpt|gpt|openai|cursor|devin|aider|codex|gemini|windsurf|jetbrains ai|amazon q|sweep|bot)\b'
'generated (with|by)\b'
'noreply\.anthropic\.com'
)

# Identity patterns (ERE). Emails are lowercased before matching.
email_pattern='(\[bot\]@users\.noreply\.github\.com$|@noreply\.anthropic\.com$|^actions@github\.com$)'
name_pattern='\b(claude|copilot|devin|aider|codex|gemini)\b'

fail=0
count=0
while IFS= read -r sha; do
count=$((count + 1))
msg="$(git log -1 --format='%B' "$sha")"

for pat in "${msg_patterns[@]}"; do
if printf '%s\n' "$msg" | grep -Eiq "$pat"; then
echo "::error::Commit ${sha} message matches banned pattern: ${pat}"
fail=1
fi
done
if printf '%s\n' "$msg" | grep -Fq '🤖'; then
echo "::error::Commit ${sha} message contains a robot-emoji watermark."
fail=1
fi

for role in author committer; do
if [ "$role" = author ]; then
name="$(git log -1 --format='%an' "$sha")"
email="$(git log -1 --format='%ae' "$sha")"
else
name="$(git log -1 --format='%cn' "$sha")"
email="$(git log -1 --format='%ce' "$sha")"
fi
if printf '%s\n' "$email" | tr '[:upper:]' '[:lower:]' | grep -Eq "$email_pattern"; then
echo "::error::Commit ${sha} ${role} email '${email}' is a bot/vendor identity."
fail=1
fi
if printf '%s\n' "$name" | grep -Eiq "$name_pattern"; then
echo "::error::Commit ${sha} ${role} name '${name}' is an AI/agent identity."
fail=1
fi
done
done < <(git rev-list "$range")

echo "check-no-ai-attribution: inspected ${count} commit(s) in ${range}"
if [ "$fail" -ne 0 ]; then
echo "::error::${POLICY}"
exit 1
fi
echo "check-no-ai-attribution: OK"
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Repo policy (CONTRIBUTING.md): zero AI attribution anywhere, plus DCO
# sign-off on every commit. This checks the full history — the repo is small
# enough that scanning everything is cheaper than getting ranges right.
name: no-ai-attribution
name: commit-policy

on:
push:
Expand All @@ -21,8 +21,35 @@ jobs:
# On PRs, check the real commits — not the synthetic merge commit
# GitHub fabricates (committer GitHub <noreply@github.com>, no DCO).
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check commits for AI attribution and DCO sign-off
run: bash .github/scripts/check-attribution.sh
- name: Determine the commit range
id: range
env:
EVENT: ${{ github.event_name }}
BASE: ${{ github.event.pull_request.base.sha }}
HEAD: ${{ github.event.pull_request.head.sha }}
BEFORE: ${{ github.event.before }}
AFTER: ${{ github.event.after }}
run: |
if [ "$EVENT" = "pull_request" ]; then
range="${BASE}..${HEAD}"
elif [ -z "$BEFORE" ] || [ "$BEFORE" = "0000000000000000000000000000000000000000" ] \
|| ! git cat-file -e "$BEFORE" 2>/dev/null; then
range="$AFTER"
else
range="${BEFORE}..${AFTER}"
fi
echo "Checking range: $range"
echo "range=$range" >> "$GITHUB_OUTPUT"

- name: DCO sign-off
env:
RANGE: ${{ steps.range.outputs.range }}
run: .github/scripts/check-dco.sh "$RANGE"

- name: No AI attribution
env:
RANGE: ${{ steps.range.outputs.range }}
run: .github/scripts/check-no-ai-attribution.sh "$RANGE"
- name: Check PR body for AI attribution
if: github.event_name == 'pull_request'
env:
Expand Down
122 changes: 122 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Working on launchbound

Instructions for coding agents — and useful to humans. **launchbound** is a convergence-safe autotuner for Rust GPU kernels: it searches a kernel's launch and specialization space, finds the fastest configuration, and never returns one that is convergence-unsafe.

This file is the canonical brief; `CLAUDE.md` points here. `CONTRIBUTING.md`
is the full contributor document and wins wherever the two disagree.

## Layout

- `crates/launchbound-*` — one crate per stage: `space` (enumerate), `prune`
(the safety gate), `build`, `bench`, `search`, `model`, `metal`, `report`,
`tui`, `runner`, `cli`.
- `corpus/` — standalone kernel fixtures, deliberately **excluded** from the
workspace: they path-depend on a sibling cuda-oxide checkout and are compiled
by the prune tooling, never by `cargo test`.
- `docs/` — `ARCHITECTURE.md`, `SAFETY.md`, and `LIMITATIONS.md`. **Read
LIMITATIONS before trusting a result**, and before claiming one.

## Build and test

```sh
just ci # fmt, clippy, test, deny, schemas
cargo test --workspace # no GPU, no network, no checkout needed
just gate # the gate tests — needs cargo-reconverge + cuda-oxide
```

Pinned nightly for the analysis and compile paths (`rust-toolchain.toml`);
MSRV 1.88 for everything that does not need it.

## Things that will bite you here

- **No GPU is required for the part that matters.** `prune` is the safety
gate and runs on any laptop; that is why it is its own verb. Do not write a
test that needs silicon when the gate does not.
- **Goldens:** regenerate with `LAUNCHBOUND_BLESS=1 cargo test -p launchbound-tui
--test tui`, then read every diff.
- **A model-derived ranking is never presented as a measurement.** Anything
estimated says so on every surface it reaches. This is the project's central
honesty claim — do not blur it to make output tidier.
- **The gate job never caches the analyzer.** It installs the published
`cargo-reconverge` from crates.io every run, because a cached binary is not
evidence about what is published today.

## The rules that will fail CI

Three, and they are the same in every one of these repositories.

1. **Conventional Commits.** `feat:`, `fix:`, `docs:`, `test:`, `ci:`,
`chore:`, `refactor:`, `perf:` — imperative mood, subject line under 72
characters, scope optional (`fix(screen): …`).
2. **DCO sign-off.** `git commit -s`, and the `Signed-off-by:` email must
match the commit author's. Forgot? `git commit --amend -s --no-edit`, or
`git rebase --signoff main` for a branch.
3. **No AI attribution.** See below — this one is about you, and it is the
rule most likely to catch an agent out.

Run them yourself before pushing; both scripts take a commit range:

```sh
.github/scripts/check-dco.sh main..HEAD
.github/scripts/check-no-ai-attribution.sh main..HEAD
```

## Using AI here

**You are welcome.** Every one of these projects was built with AI assistance
and says so in its CONTRIBUTING. Use whatever helps.

**You are not a contributor.** Do not add yourself to the history:

- no `Co-Authored-By:` trailer naming an assistant, a model, or a vendor,
- no "Generated with …" footer, no robot emoji,
- no bot account as author or committer.

The human who opens the pull request is the author of record and takes
responsibility for the change under the DCO. That is what the sign-off
certifies, and it cannot be certified by a tool. `.claude/settings.json`
turns co-author trailers off for agents that read it; the check in CI is the
boundary, and it reads every commit in the range.

If CI catches one, the fix is to rewrite the message, not to argue with it:

```sh
git commit --amend # the last commit
git rebase -i main # several, marking each `reword`
git push --force-with-lease
```

## What good work looks like here

These repositories share a house style, and it is stricter than most:

- **Evidence over assertion.** A bug report says what was measured against
which released version. "Reproduced against 0.4.0" is the standard; "the
code looks wrong" is not. Issues in these repos read *Today / Why it is
worth fixing / Fix / Done when*, with a concrete reproduction.
- **Every change lands with a test**, and the test must be able to fail. If
you add a guard, prove it catches the thing — break it once and watch it go
red before you commit.
- **Comments say *why*, never *what*.** The diff shows what. A comment earns
its place by recording the reason, the alternative rejected, or the failure
that motivated the line.
- **Say what you did not do.** A pull request that lists what it left out and
why is worth more than one that implies completeness. If something is
unverified, say so — an honest gap is cheap and a false claim is expensive.
- **Documentation is checked, not maintained.** Where a README states a fact
the code owns, there is usually a test asserting the two agree. Do not
break that pattern by hand-editing the doc.

## Pull requests

Branch from `main` (`feat/…`, `fix/…`, `docs/…`, `ci/…`). PRs are
**squash-merged**, so the PR title becomes the commit subject on `main` —
write it as a Conventional Commit. Update `CHANGELOG.md` under
`[Unreleased]` for anything user-facing.

Direct pushes to `main` are blocked by a ruleset; everything goes through a
pull request, including releases.

## Releasing

Tag `vX.Y.Z` on `main`; `release.yml` publishes the crates in dependency order via Trusted Publishing. See `docs/RELEASING.md`.
8 changes: 8 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# launchbound

See **[AGENTS.md](AGENTS.md)** — one brief for every coding agent,
so the instructions cannot drift apart per tool.

The short version: Conventional Commits, `git commit -s` for the DCO,
and **no AI attribution in the history** — you are welcome to use AI
here, but the human opening the pull request is the author of record.
Loading
Loading