diff --git a/.github/workflows/claude-card.yml b/.github/workflows/claude-card.yml new file mode 100644 index 0000000..ca29bbb --- /dev/null +++ b/.github/workflows/claude-card.yml @@ -0,0 +1,42 @@ +name: CLAUDE.md card + +# Checks the CLAUDE.md card against the card spec (documentation/claude-card.md in cosyte/config: +# Gate, Hazard and Rules, within its line, width and byte limits) and every pointer into agent notes. +# Both checkers are commands of `@cosyte/script-utils`, run through npx at the exact version below, +# so nothing here restates a rule they hold. Moving to a new version means changing SCRIPT_UTILS here +# and in `no-emdash.yml` together. + +on: + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + SCRIPT_UTILS: "@cosyte/script-utils@0.1.4" + +jobs: + claude-card: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: "24" + + - name: The card follows the spec + run: npx -y "$SCRIPT_UTILS" claude-card + + - name: Every agent-notes pointer resolves + run: npx -y "$SCRIPT_UTILS" agent-notes diff --git a/.github/workflows/no-emdash.yml b/.github/workflows/no-emdash.yml index 63cacdb..8eaac45 100644 --- a/.github/workflows/no-emdash.yml +++ b/.github/workflows/no-emdash.yml @@ -1,11 +1,15 @@ name: Em-dash gate -# The brand rule (founder directive 2026-07-24, knowledgebase 06-brand/voice-and-tone.md) bans -# U+2014 outright across every Cosyte surface and names commit messages explicitly; this repository -# also refuses the en dash (U+2013). The job scans every tracked file and, on a pull request, the -# title, the body and the commit messages. A green run on a clean tree proves nothing on its own, -# which is why the script it calls refuses to print OK from a scan that did not read everything. -# The starters themselves are tested by `starters.yml`. +# The brand rule (knowledgebase 06-brand/voice-and-tone.md) bans the em dash (U+2014) and the en dash +# (U+2013) across every Cosyte surface, and names commit messages explicitly. The job scans every +# tracked file and its name and, on a pull request, the title, the body and the commit messages. The +# starters themselves are tested by `starters.yml`. +# +# THE GATE IS NOT IN THIS REPOSITORY. It is the one implementation every Cosyte repository runs, the +# `no-emdash` command of `@cosyte/script-utils`, run through npx at the exact version below, since +# this repository has no root package.json. Moving to a new version means changing SCRIPT_UTILS here +# and in `claude-card.yml` together. A path the scan should leave out would go in +# `scripts/check-no-emdash.exclude`; none does today. # # WHY A SEPARATE WORKFLOW, and not a job in `starters.yml`: # @@ -13,17 +17,14 @@ name: Em-dash gate # type, which is not a default: without it, a title or body edited after the last push is # never re-checked, and since this repo squash-merges, the PR title and body ARE the commit # message that lands on main. Re-running every starter on both Node lines each time someone -# fixes a typo in a PR description would be a large cost for no extra safety. Same call hl7 -# made (hl7's own `no-emdash.yml`), and website before it, for the same reason. +# fixes a typo in a PR description would be a large cost for no extra safety. # # Both halves live here rather than being split across two workflows: the file scan is a -# checkout and one grep, so it costs nothing to re-run on `edited`, and one file is one -# thing to read. +# checkout, one small download and one scan, so it costs little to re-run on `edited`, and +# one file is one thing to read. # # THE PR-TEXT HALF MUST NEVER BECOME A REQUIRED STATUS CHECK ON ITS OWN. It runs as a step of the # `no-emdash` job, so the job's single check covers both halves. -# -# Run it locally with `bash scripts/check-no-emdash.sh`. on: push: @@ -40,6 +41,9 @@ concurrency: permissions: contents: read +env: + SCRIPT_UTILS: "@cosyte/script-utils@0.1.4" + jobs: no-emdash: runs-on: ubuntu-latest @@ -50,9 +54,14 @@ jobs: # Full history: the commit-message step below reads the PR's commit range, # and the default shallow clone does not contain the base commit. fetch-depth: 0 + persist-credentials: false + + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: "24" - - name: Check tracked files - run: bash scripts/check-no-emdash.sh + - name: Check tracked files and their names + run: npx -y "$SCRIPT_UTILS" no-emdash # The rule names commit messages explicitly, and a squash merge composes its message # from the PR title, the PR body, and the branch commits, so all three are checked. @@ -78,5 +87,5 @@ jobs: printf '%s\n' "$PR_BODY" git log --format=%B "$BASE_SHA..$HEAD_SHA" } > "$RUNNER_TEMP/pr-messages.txt" - bash scripts/check-no-emdash.sh --stdin "the PR title, body, or commit messages" \ + npx -y "$SCRIPT_UTILS" no-emdash --stdin "the PR title, body, or commit messages" \ < "$RUNNER_TEMP/pr-messages.txt" diff --git a/AGENTS.md b/AGENTS.md index 239a36d..d44f2ed 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,7 +20,9 @@ npm test # runs it again and asserts its key output, offline Run these from the repository root, in this order, before you push: -1. `bash scripts/check-no-emdash.sh` +1. The shared gates, at the version the workflows pin: + `npx -y @cosyte/script-utils@0.1.4 no-emdash`, then the same with `claude-card` and + `agent-notes` in place of `no-emdash`. 2. In every starter, `npm ci`, then `npm test`. CI lists the starters this way: `git ls-files -- '*/package.json' | awk -F/ 'NF == 2 { print $1 }'` 3. Step 2 again on Node 22, the floor, for each starter the change touches: diff --git a/CLAUDE.md b/CLAUDE.md index 2d8fbb4..0fffe71 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,9 +7,11 @@ what CI does with the gate, the hazard, and the rules that bind here. ## Gate -The gate is the four steps in AGENTS.md. Step 1 is `no-emdash.yml`'s `no-emdash` check. On a pull -request it also scans the title, the body, and the commit messages (the script's `--stdin` mode), -since a squash merge makes them the commit on `main`, and editing the title or body runs it again. +The gate is the four steps in AGENTS.md. Step 1 is `no-emdash.yml`'s `no-emdash` check and +`claude-card.yml`, which run `@cosyte/script-utils` through npx at one pinned version; keep no gate +script here. On a pull request `no-emdash` also scans the title, the body, and the commit messages +(its `--stdin` mode), since a squash merge makes them the commit on `main`, and editing the title or +body runs it again. Steps 2 and 3 are `starters.yml`: every starter on Node 22 and 24, and `All starters` is the one check that stands for the whole matrix, so a new starter folder is tested with no workflow edit. `starters.yml` also runs nightly on `main`, from the lockfiles. Step 4 is `actionlint.yml`; diff --git a/scripts/check-no-emdash.sh b/scripts/check-no-emdash.sh deleted file mode 100755 index 057a74c..0000000 --- a/scripts/check-no-emdash.sh +++ /dev/null @@ -1,140 +0,0 @@ -#!/usr/bin/env bash -# scripts/check-no-emdash.sh -# Brand rule: Cosyte never uses the em dash (U+2014), and this repository also -# refuses the en dash (U+2013), in any tracked file and in any pull request -# title, body or commit message. Source of truth: -# knowledgebase/06-brand/voice-and-tone.md. Rewrite with a period, a colon, a -# comma, parentheses or a plain hyphen. -# -# Usage: -# bash scripts/check-no-emdash.sh scan every tracked file -# bash scripts/check-no-emdash.sh --stdin