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
4.5 KiB
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:
- Add the glob to
paths:in.github/workflows/test.yml - Add the same glob to
paths-ignore:in.github/workflows/test-skip.yml - Add the same glob to
changeset-required.ymlif 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.