chadtmiller.com/knotless
git that lives in your Atmosphere account
7
# The scenario tracks (harness/scenarios.mjs): agents doing a day's git
# work through the MCP server. Each run costs model usage, so this never
# runs on a push or a pull, only when the owner asks for it by name:
#
# knotless rerun knotless://chadtmiller.com/knotless --ref refs/heads/main \
# --workflow scenarios [-e TRACKS="team solo"] [-e SCENARIO_MODEL=...]
#
# TRACKS names the tracks, all of them but `ci` by default: its runner
# needs Docker, which a run's container does not have. The report is the
# end of the log; the check passes whatever the agents tripped on, and
# fails only when the tracks could not run.
#
# The agents are Claude Code sessions, on whichever credentials the
# runner's secrets file on the host grants main: ANTHROPIC_API_KEY (the
# API), CLAUDE_CODE_OAUTH_TOKEN (a subscription, from `claude
# setup-token`), or OPENROUTER_API_KEY, which goes through OpenRouter
# instead and is used first when granted. SCENARIO_MODEL picks the model,
# through OpenRouter any it has (-e SCENARIO_MODEL=openai/gpt-5), though
# Claude Code is made for Anthropic's.
image: rust:1.98
on:
asked: {refs: [refs/heads/main]}
secrets: [ANTHROPIC_API_KEY, CLAUDE_CODE_OAUTH_TOKEN, OPENROUTER_API_KEY]
cache: [target, /usr/local/cargo/registry, /usr/local/rustup, /opt/node, /opt/atproto, harness/node_modules]
env:
TRACKS: contribution mentions moderation owners private review revert solo team
# As test.yml's, for the tracks that need the spaces alpha.
ATPROTO_COMMIT: 11c5d98cd50261c5e1201fedc5c6c05a7b8d1ccf
ALPHA_BUILD: "2"
steps:
- uses: chadtmiller.com/steps/node@1
with: {version: 24.22.1}
- name: claude
run: |
export PATH=/opt/node/bin:$PATH
[ -x /opt/node/bin/claude ] || npm install -g --no-audit --no-fund @anthropic-ai/claude-code
claude --version
- uses: chadtmiller.com/steps/npm-ci@1
name: harness
with: {dir: harness, omit: dev}
- name: spaces alpha
run: |
case " $TRACKS " in *" private "*) ;; *) exit 0 ;; esac
[ "$(cat /opt/atproto/.built 2>/dev/null)" = "$ATPROTO_COMMIT $ALPHA_BUILD" ] && exit 0
export PATH=/opt/node/bin:$PATH COREPACK_ENABLE_DOWNLOAD_PROMPT=0 COREPACK_HOME=/opt/atproto/.corepack
find /opt/atproto -mindepth 1 -delete
cd /opt/atproto
git init -q . && git fetch -q --depth 1 https://github.com/bluesky-social/atproto.git "$ATPROTO_COMMIT" && git checkout -q FETCH_HEAD
mkdir .bin && corepack enable --install-directory /opt/atproto/.bin
export PATH=/opt/atproto/.bin:$PATH
pnpm install --frozen-lockfile --ignore-scripts
pnpm run build:tooling --force
pnpm --filter '@atproto/dev-env^...' --filter @atproto/dev-env run --if-present prebuild
pnpm exec tsc --build packages/dev-env/tsconfig.build.json
pnpm --filter @atproto/oauth-provider-ui run build:ui
cd "$(dirname "$(node -p "require.resolve('better-sqlite3/package.json', { paths: ['/opt/atproto/packages/pds'] })")")"
[ -f build/Release/better_sqlite3.node ] || npm run install
echo "$ATPROTO_COMMIT $ALPHA_BUILD" > /opt/atproto/.built
- name: build
run: cargo build -q --workspace --bins
- name: scenarios
# Tracks go side by side, each step of one a session of up to 20
# minutes, in turn.
timeout: 240
run: |
export PATH=/opt/node/bin:$PATH KNOTLESS_SPACES_DEV_ENV=/opt/atproto/packages/dev-env
# OpenRouter speaks Claude Code's own protocol; the Anthropic key
# must be empty, not unset, or it is tried first.
if [ -n "${OPENROUTER_API_KEY:-}" ]; then
export ANTHROPIC_BASE_URL=https://openrouter.ai/api ANTHROPIC_AUTH_TOKEN=$OPENROUTER_API_KEY ANTHROPIC_API_KEY=
fi
rm -rf target/scenarios
node harness/scenarios.mjs $TRACKS
cat target/scenarios/*/report.md