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
11 changes: 2 additions & 9 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,11 @@
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"matcher": "Bash(git push *)",
"hooks": [
{
"type": "command",
"if": "Bash(git commit *)",
"command": "input=$(cat); cmd=$(echo \"$input\" | jq -r '.tool_input.command // empty' 2>/dev/null); if ! echo \"$cmd\" | grep -qE '(^|[^a-zA-Z0-9_-])git[[:space:]]+commit($|[^a-zA-Z0-9_-])'; then exit 0; fi; if git diff --cached --name-only 2>/dev/null | grep -q '^CHANGELOG.md$'; then exit 0; fi; if echo \"$cmd\" | grep -q 'CHANGELOG'; then exit 0; fi; echo '{\"decision\":\"block\",\"reason\":\"CHANGELOG.md is not staged. Update the changelog and bump the version before committing. Stage CHANGELOG.md (and any version-file changes), then retry.\"}'",
"statusMessage": "Checking changelog..."
},
{
"type": "command",
"if": "Bash(git push *)",
"command": "input=$(cat); cmd=$(echo \"$input\" | jq -r '.tool_input.command // empty' 2>/dev/null); if ! echo \"$cmd\" | grep -qE '(^|[^a-zA-Z0-9_-])git[[:space:]]+push($|[^a-zA-Z0-9_-])'; then exit 0; fi; branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null); if [ \"$branch\" = \"main\" ] || [ \"$branch\" = \"HEAD\" ] || [ -z \"$branch\" ]; then exit 0; fi; if echo \"$branch\" | grep -qE '^(feat|fix|refactor|chore|docs|test)/[0-9]+-[a-z0-9-]+$'; then exit 0; fi; printf '{\"decision\":\"block\",\"reason\":\"Branch name %s does not follow <type>/<issue>-<slug>. Allowed prefixes: feat/, fix/, refactor/, chore/, docs/, test/. Example: chore/4-dep-bump. Rename with git branch -m, or push to main if this is a trivial edit.\"}\\n' \"$branch\"",
"command": "branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null); if [ \"$branch\" = \"main\" ] || [ \"$branch\" = \"HEAD\" ] || [ -z \"$branch\" ]; then exit 0; fi; if echo \"$branch\" | grep -qE '^(feat|fix|refactor|chore|docs|test)/([0-9]+-)?[a-z0-9-]+$'; then exit 0; fi; if echo \"$branch\" | grep -qE '^chore/release-v[0-9]+[.][0-9]+[.][0-9]+'; then exit 0; fi; printf '{\"decision\":\"block\",\"reason\":\"Branch name %s does not follow <type>/<slug> or <type>/<issue>-<slug>. Allowed prefixes: feat/, fix/, refactor/, chore/, docs/, test/. Examples: refactor/viz-cleanup, chore/4-dep-bump. Rename with git branch -m, or push to main if this is a trivial edit.\"}\\n' \"$branch\"",
"statusMessage": "Checking branch name..."
}
]
Expand Down
10 changes: 9 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@

## Test plan

<!-- How was this verified? Checklist of commands / manual steps, or "N/A — docs only". -->
<!-- Pre-merge gate checks only. Don't include "CI green" — already enforced by required-status-checks.
If a check is genuinely manual, name the exact action (e.g. "open the rendered README on github.com").
"N/A — docs only" is fine when the only check is automated lint. -->

## Follow-ups

<!-- Post-merge work: release tagging, smoke-testing the next change, watching a metric.
If non-trivial, link a tracking issue rather than leaving a checkbox in a closed PR.
Delete this section if there are none. -->

Closes #
16 changes: 16 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
changelog:
categories:
- title: "New Features"
labels: ["type: feature"]
- title: "Bug Fixes"
labels: ["type: bug"]
- title: "Security"
labels: ["security"]
- title: "Improvements"
labels: ["type: enhancement"]
- title: "Documentation"
labels: ["type: docs"]
- title: "Maintenance"
labels: ["infra", "chore"]
exclude:
labels: ["duplicate", "invalid", "wontfix"]
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ All notable changes to this project are documented here. Format based on [Keep a
### Added

- `README.md` — quick-start, sovereignty sequence summary, file map.
- `scripts/init-labels.sh` and `scripts/protect-main.sh` from WorldRover canon.
- `.github/release.yml` — PR-label-based release note categories.

### Changed

- `.claude/settings.json` — retire old pre-commit changelog hook; update pre-push hook to canon v0.2.5 (issue number optional in slug, allow `chore/release-v*` branches).
- `.github/PULL_REQUEST_TEMPLATE.md` — add `## Follow-ups` section; update test plan comment to canon v0.2.5 wording.
- `.markdownlint.json` and `.editorconfig` from the WorldRover canon. Initial scaffold missed these; CI was red on default markdownlint MD013 (line-length) until the configs landed. Plus a local `MD024: {siblings_only: true}` override so the standard Keep a Changelog repeated `### Added` headings under different version sections don't trigger duplicate-heading errors. Closes #1.

## [0.1.0] - 2026-04-26
Expand Down
61 changes: 61 additions & 0 deletions scripts/init-labels.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# Create the canonical WorldRover label set on the current repo.
# Idempotent: existing labels with the same names are left alone (gh exits non-zero
# but we ignore it). To force-update colors/descriptions, run with `--update`.

set -euo pipefail

REPO="${1:-}"
UPDATE_FLAG=""

# Allow `./init-labels.sh --update` or `./init-labels.sh OWNER/REPO --update`
for arg in "$@"; do
case "$arg" in
--update) UPDATE_FLAG="--force" ;;
*/*) REPO="$arg" ;;
esac
done

if [[ -z "$REPO" ]]; then
REPO=$(gh repo view --json nameWithOwner --jq .nameWithOwner)
fi

echo "Initializing labels on $REPO"

create() {
local name="$1" description="$2" color="$3"
if [[ -n "$UPDATE_FLAG" ]]; then
gh label create "$name" --description "$description" --color "$color" --force --repo "$REPO"
else
gh label create "$name" --description "$description" --color "$color" --repo "$REPO" 2>/dev/null \
|| echo " · skipped (exists): $name"
fi
}

# Domain
create "ui" "User interface, visual, layout" "9b59b6"
create "data" "Measurement, computation, baselines, scoring" "0e8a16"
create "infra" "Dependencies, tooling, CI, platform support" "1abc9c"

# Priority
create "P1" "High priority" "b60205"
create "P2" "Medium priority" "ff9f1c"
create "P3" "Low priority / nice to have" "c2e0c6"

# Type
create "type: bug" "Something isn't working" "d73a4a"
create "type: feature" "New functionality" "1d76db"
create "type: docs" "Documentation only" "999999"
create "type: enhancement" "Refinement of existing functionality" "7057ff"
create "type: refactor" "Behavior-preserving restructuring" "e4e669"

# Remove the GitHub default labels that overlap with the type:- variants.
# These deletions are also non-fatal — if they don't exist, gh prints a warning.
echo "Removing redundant default labels"
gh label delete "bug" --yes --repo "$REPO" 2>/dev/null || true
gh label delete "enhancement" --yes --repo "$REPO" 2>/dev/null || true
gh label delete "documentation" --yes --repo "$REPO" 2>/dev/null || true

echo
echo "Done. Final label set:"
gh label list --repo "$REPO"
53 changes: 53 additions & 0 deletions scripts/protect-main.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# Apply the canonical WorldRover branch-protection profile to `main` on the
# current GitHub repo. Solo-friendly: blocks force-push and deletion, requires
# a PR for any change to main, and requires a status check named `markdown-lint`
# (the one job every starter-derived repo has). Skip the review requirement —
# solo work doesn't have a reviewer, and a 0-approval PR-required rule still
# enforces the PR flow.
#
# Usage: ./scripts/protect-main.sh [extra_check_name ...]
#
# Required checks given as arguments are added on top of `markdown-lint`. For a
# Node project that runs typecheck + build under a `build` job, you would call:
#
# ./scripts/protect-main.sh build
#
# Re-running is idempotent — it overwrites the protection ruleset.

set -euo pipefail

repo=$(gh repo view --json nameWithOwner --jq .nameWithOwner)

# Build the contexts list: markdown-lint plus any extra args.
contexts_json='["markdown-lint"'
for c in "$@"; do
contexts_json+=",\"$c\""
done
contexts_json+="]"

# shellcheck disable=SC2016
gh api -X PUT "repos/${repo}/branches/main/protection" \
-H "Accept: application/vnd.github+json" \
--input - <<JSON
{
"required_status_checks": {
"strict": true,
"contexts": ${contexts_json}
},
"enforce_admins": false,
"required_pull_request_reviews": {
"required_approving_review_count": 0,
"dismiss_stale_reviews": false,
"require_code_owner_reviews": false
},
"restrictions": null,
"allow_force_pushes": false,
"allow_deletions": false,
"required_linear_history": true,
"required_conversation_resolution": false
}
JSON

echo "Branch protection applied to ${repo}@main."
echo "Required status checks: ${contexts_json}"
Loading