Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
73770ce
chore(repo-sweep): seed hygiene sweep
kyle-sexton Sep 26, 2026
5ef303f
refactor: simplify local CI scripts and repin ci-workflows to v0.29.1
kyle-sexton Sep 26, 2026
f1af78e
chore(ci): drop history and ticket residue from ci.yml comments
kyle-sexton Sep 26, 2026
5fc82af
refactor: apply structure-only tidyings across scripts and docs
kyle-sexton Sep 26, 2026
56809ef
docs: correct drifted claims in README, CLAUDE.md, and ci.yml comments
kyle-sexton Sep 27, 2026
8d73b6d
ci: make pr-section-drift advisory and drop the empty recurring schedule
kyle-sexton Sep 27, 2026
579617a
chore(claude): deny reads of secret-shaped files in project settings
kyle-sexton Sep 27, 2026
4e27d19
docs(claude): name ci-status as the job whose needs: gates a lane
kyle-sexton Sep 27, 2026
10e1a51
docs(claude): name the local check command and drop lines the PR rule…
kyle-sexton Sep 27, 2026
25f41ec
docs(readme): drop the hardcoded CI tool list from the Cursor environ…
kyle-sexton Sep 27, 2026
7c794ea
fix(check): report pr-section-drift as advisory, matching CI
kyle-sexton Sep 28, 2026
5ebec0e
docs: tighten org-wide policy docs and README prose
kyle-sexton Sep 28, 2026
000ede8
docs: restore unconditional override in SECURITY and GOVERNANCE
kyle-sexton Sep 28, 2026
df02ccd
fix(check): lint only tracked markdown in the local markdown lane
kyle-sexton Sep 28, 2026
e9e7002
fix(check): check links in tracked markdown only
kyle-sexton Sep 29, 2026
a0b83b9
docs: align check.sh and settings.json descriptions with behavior
kyle-sexton Sep 29, 2026
486a966
Merge remote-tracking branch 'origin/main' into chore/repo-sweep-hygi…
kyle-sexton Sep 29, 2026
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: 1 addition & 1 deletion .claude/ai-slop.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"excluded_paths": [],
"em_dash_allowed_paths": [],
"_comment": "Only text this repository does not own may be excluded here, and nothing currently qualifies. CODE_OF_CONDUCT.md is adapted from Contributor Covenant v2.1 under CC BY 4.0 and is maintained as this organization's own policy, so it is audited and edited like every other file. .claude/rules/pr-body-contract.md is sync-managed from melodic-software/standards; it stays in scope so findings there are reported and filed upstream, never edited here. The em-dash rule is zero-tolerance and runs on every file: the former '- **Term** — definition' list idiom was rewritten to '- **Term**: definition' rather than exempted. Before adding a path to either list, name whose text it is and why it cannot be changed here."
"_comment": "Only text this repository does not own may be excluded here, and nothing currently qualifies. CODE_OF_CONDUCT.md is adapted from Contributor Covenant v2.1 under CC BY 4.0 and is maintained as this organization's own policy, so it is audited and edited like every other file. .claude/rules/pr-body-contract.md is sync-managed from melodic-software/standards; it stays in scope so findings there are filed upstream, never edited here. The em-dash rule is zero-tolerance on every file. Before adding a path to either list, name whose text it is and why it cannot be changed here."
}
11 changes: 11 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@
}
},
"enabledPlugins": {},
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./.claude/settings.local.json)",
"Read(**/*.key)",
"Read(**/*.pem)",
"Read(**/id_rsa)"
]
},
"hooks": {
"SessionStart": [
{
Expand Down
161 changes: 64 additions & 97 deletions .cursor/check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,28 @@
# (CONTRIBUTING step 3: "Ensure the project builds and its checks pass locally").
#
# Not set -e: lanes are collected, not short-circuited. Exit status is non-zero
# when any lane fails.
# when any gating lane fails.
set -uo pipefail

cd -- "$(git rev-parse --show-toplevel 2>/dev/null || echo .)" || exit 1
top="$(git rev-parse --show-toplevel)" && cd -- "$top" || exit 1

# Fall back onto a per-user bin in case install.sh placed tools there.
case ":$PATH:" in
*":$HOME/.local/bin:"*) ;;
*) PATH="$HOME/.local/bin:$PATH" ;;
esac
export PATH

PASSED=()
FAILED=()

record() {
local name="$1" rc="$2"
if [[ "$rc" -eq 0 ]]; then
heading() { printf '\n\033[1m── %s ──\033[0m\n' "$1" >&2; }

# run_lane <name> <cmd...>: run one lane under a heading and record its verdict.
run_lane() {
local name="$1"
shift
heading "$name"
if "$@"; then
PASSED+=("$name")
printf '\033[32m✓ %s\033[0m\n' "$name" >&2
else
Expand All @@ -34,23 +38,30 @@ record() {
fi
}

heading() { printf '\n\033[1m── %s ──\033[0m\n' "$1" >&2; }

# --- Lanes with multi-step logic --------------------------------------------

# typos skips hidden paths unless they are named. The synced _typos.toml
# cannot set ignore-hidden = false; CI's typos job has the same follow-up.
# Skip index entries that are gone from the worktree: an unstaged deletion is
# still listed, and typos exits 64 when an explicit path is missing.
lane_typos() {
local hidden=() path
while IFS= read -r -d '' path; do
[[ "$path" == .* && -f "$path" ]] && hidden+=("$path")
done < <(git ls-files -z)
typos --config _typos.toml . "${hidden[@]}"
}

lane_jsonschema() {
local rc=0 forms=() f
check-jsonschema --builtin-schema vendor.dependabot .github/dependabot.yml || rc=1
check-jsonschema --builtin-schema vendor.github-workflows .github/workflows/*.yml || rc=1
for f in .github/ISSUE_TEMPLATE/*.yml .github/ISSUE_TEMPLATE/*.yaml; do
[[ -e "$f" ]] || continue
[[ "$(basename -- "$f")" == config.yml ]] && continue
forms+=("$f")
[[ -e "$f" && "$(basename -- "$f")" != config.yml ]] && forms+=("$f")
done
if [[ ${#forms[@]} -eq 0 ]]; then
# Match ci.yml's roster step: an empty set is a failed derivation, not a
# skipped validation. Skipping here let a deleted-forms change pass locally
# while CI went red.
# skipped validation.
echo "No issue forms (*.yml/*.yaml other than config.yml) found under .github/ISSUE_TEMPLATE/." >&2
return 1
fi
Expand Down Expand Up @@ -82,39 +93,37 @@ lane_shellcheck() {
# there, leaving the caller's index and working tree untouched. CI itself
# runs on a clean checkout, so this extra isolation is local-only.
lane_eol() {
local tmp tmpindex tmpwt gitdir before after drift rc=0
gitdir="$(git rev-parse --absolute-git-dir)"
tmp="$(mktemp -d)"
tmpindex="$tmp/index"
tmpwt="$tmp/wt"
mkdir -p "$tmpwt"
cp -- "$(git rev-parse --git-path index)" "$tmpindex"
before="$(GIT_DIR="$gitdir" GIT_INDEX_FILE="$tmpindex" git write-tree)" || {
rm -rf "$tmp"
return 1
}
GIT_DIR="$gitdir" GIT_INDEX_FILE="$tmpindex" GIT_WORK_TREE="$tmpwt" git checkout-index --all || {
rm -rf "$tmp"
return 1
}
GIT_DIR="$gitdir" GIT_INDEX_FILE="$tmpindex" GIT_WORK_TREE="$tmpwt" git add --renormalize -- . || {
rm -rf "$tmp"
return 1
}
after="$(GIT_DIR="$gitdir" GIT_INDEX_FILE="$tmpindex" git write-tree)" || {
rm -rf "$tmp"
return 1
}
local tmp gitdir before after drift rc=0
tmp="$(mktemp -d)" || return 1
gitdir="$(git rev-parse --absolute-git-dir)" &&
mkdir -p "$tmp/wt" &&
cp -- "$(git rev-parse --git-path index)" "$tmp/index" &&
before="$(GIT_DIR="$gitdir" GIT_INDEX_FILE="$tmp/index" git write-tree)" &&
GIT_DIR="$gitdir" GIT_INDEX_FILE="$tmp/index" GIT_WORK_TREE="$tmp/wt" git checkout-index --all &&
GIT_DIR="$gitdir" GIT_INDEX_FILE="$tmp/index" GIT_WORK_TREE="$tmp/wt" git add --renormalize -- . &&
after="$(GIT_DIR="$gitdir" GIT_INDEX_FILE="$tmp/index" git write-tree)" || rc=1
rm -rf "$tmp"
drift="$(git diff --name-only "$before" "$after")"
if [[ -z "$drift" ]]; then
echo "index EOL clean"
else
[[ "$rc" -eq 0 ]] || return 1
if ! drift="$(git diff --name-only "$before" "$after")"; then
echo "eol-renormalize: git diff failed; cannot tell whether the index has EOL drift" >&2
return 1
fi
if [[ -n "$drift" ]]; then
echo "EOL drift (fix: git add --renormalize . && git commit):" >&2
printf '%s\n' "$drift" >&2
rc=1
return 1
fi
return "$rc"
echo "index EOL clean"
}

# Tracked files only, as on CI's clean checkout; a "**/*.md" glob also lints
# gitignored scratch files.
lane_markdown() {
git ls-files -z -- '*.md' | xargs -0 markdownlint-cli2 --config .markdownlint-cli2.jsonc
}

lane_links() {
git ls-files -z -- '*.md' | xargs -0 lychee --offline --no-progress --config lychee.toml
}

lane_pr_section_drift() {
Expand All @@ -126,65 +135,23 @@ lane_pr_section_drift() {

# --- Run every lane ---------------------------------------------------------

heading markdown
markdownlint-cli2 --config .markdownlint-cli2.jsonc "**/*.md"
record markdown "$?"

heading typos
# typos skips hidden paths unless they are named. The synced _typos.toml
# cannot set ignore-hidden = false; CI's typos job has the same follow-up.
# Skip index entries that are gone from the worktree: an unstaged deletion is
# still listed, and typos exits 64 when an explicit path is missing.
hidden=()
while IFS= read -r -d '' path; do
case "$path" in
.*)
if [[ -f "$path" ]]; then
hidden+=("$path")
fi
;;
*) ;;
esac
done < <(git ls-files -z)
typos --config _typos.toml . "${hidden[@]}"
record typos "$?"

heading editorconfig
ec -config .editorconfig-checker.json
record editorconfig "$?"

heading gitleaks
gitleaks dir --config .gitleaks.toml --no-banner .
record gitleaks "$?"

heading links
lychee --offline --no-progress --config lychee.toml \
"**/*.md" ".claude/**/*.md" ".github/**/*.md"
record links "$?"

heading actionlint
actionlint -color
record actionlint "$?"

heading jsonschema
lane_jsonschema
record jsonschema "$?"

heading shellcheck
lane_shellcheck
record shellcheck "$?"

heading eol-renormalize
lane_eol
record eol-renormalize "$?"

run_lane markdown lane_markdown
run_lane typos lane_typos
run_lane editorconfig ec -config .editorconfig-checker.json
run_lane gitleaks gitleaks dir --config .gitleaks.toml --no-banner .
run_lane links lane_links
run_lane actionlint actionlint -color
run_lane jsonschema lane_jsonschema
run_lane shellcheck lane_shellcheck
run_lane eol-renormalize lane_eol

# Advisory, as in CI: reported, never counted toward the ci-status verdict.
heading pr-section-drift
lane_pr_section_drift
record pr-section-drift "$?"
lane_pr_section_drift || printf '\033[33m! pr-section-drift (advisory)\033[0m\n' >&2

# --- Summary ----------------------------------------------------------------

printf '\n\033[1m── ci-status ──\033[0m\n' >&2
heading ci-status
printf 'passed: %d failed: %d\n' "${#PASSED[@]}" "${#FAILED[@]}" >&2
if [[ ${#FAILED[@]} -gt 0 ]]; then
printf 'failing lanes: %s\n' "${FAILED[*]}" >&2
Expand Down
Loading
Loading