Files
msd-core/docs/branch-protection.md
Tom Boucher 2a915c1b82 chore: migrate references from gsd-build to open-gsd/get-shit-done-redux (#120) (#121)
Security-motivated migration of all stale repository and npm-scope references.

Three categories of changes (58 files, 174 substitutions):

1. gsd-build → open-gsd (security-critical):
   - .github/workflows/release-sdk.yml — npm token comment, tarball filename pattern
   - .github/workflows/hotfix.yml — same
   - .changeset/fix-3406-detect-stale-sdk-shadow.md — @gsd-build/sdk → @open-gsd/sdk
   - .changeset/sharp-quails-leap.md — same
   - get-shit-done/workflows/update.md — CHANGELOG raw GitHub URL

2. GSD-redux org slug → open-gsd (canonical rename):
   - package.json + sdk/package.json — repository/homepage/bugs metadata
   - All README.*.md — live badge and link sections
   - CONTRIBUTING.md, CONTEXT.md, QUICK-WINS-CONFIRMED-BUGS.md
   - .coderabbit.yaml, .release-monitor.sh, scripts/sync-rulesets.sh
   - docs/** — all live agent/ADR/user-facing documentation
   - tests/** — repo slug assertions and test fixtures
   - scripts/changeset/cli.cjs + github-release-notes.cjs
   - .github/ISSUE_TEMPLATE/*, .github/pull_request_template.md
   - bin/install.js, get-shit-done/bin/lib/model-catalog.cjs
   - sdk/HANDOVER-*.md, sdk/src/*.test.ts

3. CLAUDE.md (gitignored local file — not in this commit):
   Updated separately outside git: --repo gsd-build/get-shit-done →
   --repo open-gsd/get-shit-done-redux with security warning.

Intentionally unchanged: CHANGELOG.md, docs/RELEASE-*.md,
.changeset/README.md, .changeset/build-hooks-atomic-write.md,
README.md migration table (historical fork record),
tests/changeset-serialize.test.cjs line 78 (serialization fixture).

The gsd-build/get-shit-done repo is compromised (rug-pull documented in
README.md). Do not push to or interact with that repo.

Closes #120
2026-05-22 12:28:16 -04:00

4.5 KiB
Raw Blame History

Branch Protection Rollout

Rulesets

Three ruleset specs live under .github/rulesets/. All are committed with enforcement: disabled and activated in stages via the 3-PR rollout below.

main-protection

Targets ~DEFAULT_BRANCH (main). Enforces:

  • No deletions or force pushes
  • Required linear history (no merge commits)
  • All changes via pull request (0 required approvals, stale-review dismissal, thread resolution required, squash/rebase only)
  • 10 required status checks: lint, Node 22/24 matrix (ubuntu/mac/windows), Changeset Required, require-issue-link, pr-template-format

release-branches

Targets refs/heads/release/** and refs/heads/hotfix/**. Same rules as main-protection except required_linear_history is omitted (merge commits are permitted on release/hotfix branches).

tag-immutability

Targets all tags (~ALL). Blocks tag updates and deletions — tags are immutable once created. Tag creation is unrestricted.

4-PR Rollout Plan

PR Branch Action
PR-1 chore/branch-protection-specs Check in spec files; enforcement: disabled — no effect on repo
PR-2 chore/ci-skip-tests-on-docs Add path filter to test.yml + new test-skip.yml noop; doc-only PRs satisfy required checks in <30s
PR-3 chore/branch-protection-evaluate Run sync-rulesets.sh with ENFORCEMENT=evaluate; 1-week dry-run via rule-suite logs
PR-4 chore/branch-protection-active Run sync-rulesets.sh with ENFORCEMENT=active; protection live

Running sync-rulesets.sh

Prerequisites: gh authenticated with repo-admin scope, jq installed.

# Dry-run (evaluate mode — logs violations, does not block)
REPO=open-gsd/get-shit-done-redux ENFORCEMENT=evaluate bash scripts/sync-rulesets.sh

# Activate protection
REPO=open-gsd/get-shit-done-redux ENFORCEMENT=active bash scripts/sync-rulesets.sh

# Roll back to disabled
REPO=open-gsd/get-shit-done-redux ENFORCEMENT=disabled bash scripts/sync-rulesets.sh

The script is idempotent: running it twice with the same ENFORCEMENT value is a no-op semantically (PUT with identical body).

Reading evaluate-mode logs

After applying with evaluate, check which PRs/pushes would have been blocked:

REPO=open-gsd/get-shit-done-redux
RULESET_ID=$(gh api repos/$REPO/rulesets --jq '.[] | select(.name=="main-protection") | .id')
gh api repos/$REPO/rulesets/$RULESET_ID/rule-suites

Each entry shows the actor, ref, result (pass/fail), and which rules triggered. Use this to validate no legitimate workflows are broken before flipping to active in PR-3.

Path filters

The test.yml workflow uses a paths: filter on its pull_request: trigger so the 6-lane matrix only runs when code-touching files change. A companion workflow, test-skip.yml, fires on the inverse (paths-ignore:) and emits instant noop jobs with identical job IDs and matrix dimensions. This ensures the 7 required status checks (lint-tests + 6× test (...)) are always satisfied — whether the real matrix ran or the noop ran.

Why dual workflow instead of paths-ignore alone?

GitHub required-status-checks expect a specific context string to appear as "passed" on every PR. If test.yml is suppressed by paths-ignore on a doc-only PR, those contexts never fire and the PR is permanently blocked. The noop workflow produces the same context strings via matching job IDs + matrix, resolving the deadlock.

Canonical code-paths list

Both test.yml (paths:) and test-skip.yml (paths-ignore:) use this list:

bin/**
get-shit-done/**
agents/**
commands/**
hooks/**
sdk/**
tests/**
scripts/**
package.json
package-lock.json
tsconfig*.json
.github/workflows/test.yml
.github/workflows/test-skip.yml

This list also mirrors changeset-required.yml's path filter. Keep all three in sync.

Adding a new code path

When adding a directory or file that should trigger real tests:

  1. Add the glob to paths: in .github/workflows/test.yml
  2. Add the same glob to paths-ignore: in .github/workflows/test-skip.yml
  3. Add the same glob to changeset-required.yml if changesets should be required for that path

Failure to update test-skip.yml means doc PRs that happen to touch the new path will deadlock (real matrix never fires, noop never fires either).

Phase-2 TODO

Enable the required_signatures rule (signed commits) once agent commits sign uniformly. As of PR-1 this rule is intentionally omitted — unsigned agent commits would be blocked by it. Track readiness in the issue linked to PR-3.