Maintainer Succession & Bus-Factor Plan¶
Why this document exists¶
The repo currently has one human maintainer. The 90-day commit graph runs at roughly 180 commits from a single author plus 56 from Dependabot and 21 from GitHub Actions. Branch protection on main requires 11 status checks but zero PR reviewers — because there is no second human to review. That is a deliberate trade-off for a solo project; it is also the largest risk in the repo.
This document is the plan for closing that gap. It covers:
- The criteria for naming a second maintainer
- The on-ramp / probation flow
- Which protections to harden the moment a second human is active
- The explicit list of topics that need knowledge transfer
- The commands to flip the branch-protection toggles when the time comes
1. Criteria for naming a second maintainer¶
A second maintainer should satisfy at least four of the following five before being added:
| Criterion | Why it matters |
|---|---|
| Recurring contributor — ≥ 10 merged PRs over ≥ 60 days | Demonstrates sustained engagement, not a one-off |
Crosses subsystems — has merged PRs touching at least two of docs/, csa_platform/, examples/, .github/workflows/, deploy/ | Reduces specialization risk |
| Reviews PRs effectively — has left substantive review comments on at least 3 PRs | Confirms code-review capability, not just write capability |
| Operates the runbooks — has executed at least one of the runbooks end-to-end (e.g. DR drill, key rotation) | Confirms operational competence, not just code competence |
| Aligned on architectural direction — has authored or co-authored at least one ADR | Confirms judgment alignment on big calls |
When a candidate satisfies four of five, open an ADR proposing the addition. Use the existing docs/adr/ template. The ADR captures the agreement publicly and is the audit trail.
2. On-ramp / probation flow¶
Once the ADR is merged:
| Phase | Duration | Permissions | Reviewer role |
|---|---|---|---|
| Phase 1 — Triage | 4 weeks | triage role on the repo | Can label, close issues, request changes on PRs, but cannot merge |
| Phase 2 — Write | 4 weeks | write role | Can merge non-main PRs and approve main PRs (but only fgarofalo56 merges to main during this phase) |
| Phase 3 — Maintain | Ongoing | maintain role + listed in CODEOWNERS | Can merge to main once required-review gate is set to 1 |
Phase transitions happen by explicit decision in a PR comment thread — not by time alone. If a candidate stalls, they remain at the current phase until the bar is met.
3. Protections to harden the moment a second human reaches Phase 3¶
Today's main protection (audited 2026-05-17):
required_status_checks: 11 contexts
required_pull_request_reviews: 0
enforce_admins: true
dismiss_stale: true
Once the second maintainer reaches Phase 3, run the commands in §5 to set:
required_pull_request_reviews: 1
require_code_owner_reviews: true (for /docs/adr/, /.github/, /deploy/)
required_conversation_resolution: true
enforce_admins: true should stay on regardless of who is added.
4. Topics that need explicit knowledge transfer¶
These are the "tribal knowledge" topics that previously lived only in the primary maintainer's head. All 7 have been converted to durable docs, runbooks, or ADRs as of 2026-05-17 — the bus factor on each is now zero regardless of whether a second human is onboarded yet.
| Topic | Where it is captured | Status |
|---|---|---|
Why release-please is bypassed for some status checks | ADR-0023 (architectural rationale) + runbooks/release-please-bypass.md (day-2 ops) | ✅ Done |
| How the Copilot Studio agent registration is rotated | runbooks/copilot-studio-agent-rotation.md | ✅ Done |
| Which Azure subscriptions the GitHub Actions service principal can reach | runbooks/azure-deployment-principal.md | ✅ Done |
Bicep apiVersion ratchet policy | best-practices/iac-cicd.md — Bicep apiVersion ratchet policy subsection | ✅ Done |
| Per-vertical dbt warehouse selection rationale | decisions/lakehouse-vs-warehouse-vs-lake.md | ✅ Existing |
How the rewrite_example_links.py hook decides shim vs standalone | Header docstring of docs/hooks/rewrite_example_links.py | ✅ Done |
| Test layout (per-package vs central) | ADR-0024 | ✅ Existing |
The pattern: every "you would have to ask me" topic becomes a doc, a runbook, or an ADR. Then the topic is no longer tribal — anyone with read access to the repo can self-serve.
5. Branch-protection commands (run when Phase 3 begins)¶
Replace <HANDLE> with the second maintainer's GitHub handle. Run from a machine with gh authenticated as a repo admin.
# Add to CODEOWNERS at the highest-blast-radius paths first
# (already structured for easy edit at the top of .github/CODEOWNERS)
# Set required review = 1 + require CODEOWNERS on protected paths
gh api -X PUT repos/fgarofalo56/csa-inabox/branches/main/protection \
-F required_status_checks='{"strict":true,"contexts":[]}' \
-F enforce_admins=true \
-F required_pull_request_reviews='{"required_approving_review_count":1,"dismiss_stale_reviews":true,"require_code_owner_reviews":true,"required_conversation_resolution":true}' \
-F restrictions= \
-F allow_force_pushes=false \
-F allow_deletions=false
# Verify
gh api repos/fgarofalo56/csa-inabox/branches/main/protection \
--jq '{required_status_checks: .required_status_checks.contexts | length, required_pr_reviews: .required_pull_request_reviews.required_approving_review_count, require_code_owner_reviews: .required_pull_request_reviews.require_code_owner_reviews, enforce_admins: .enforce_admins.enabled}'
The required-status-checks list is intentionally left empty in the command above — preserve whatever is currently active. To inspect them first:
gh api repos/fgarofalo56/csa-inabox/branches/main/protection \
--jq '.required_status_checks.contexts'
6. What does NOT need to change¶
enforce_admins: true— staysdismiss_stale: true— stays- The 11 existing status checks — stay (the
required_status_checksarray preserves them) - Dependabot auto-merge workflow — stays; will simply require the second-maintainer or auto-merger to approve
7. Open questions to resolve before the first add¶
These should be answered in the ADR proposing the second maintainer:
- What is the agreed weekly time commitment for the second role (5 hr / 10 hr / on-call rotation)?
- Is the second maintainer authorized to merge release PRs (
release-pleaseoutputs)? - Is the second maintainer authorized to approve security-sensitive PRs (
.github/,deploy/, secrets) or do those still require primary approval? - What is the off-boarding policy if the second maintainer becomes inactive (≥ 90 days no activity)?
8. Status¶
| Item | State |
|---|---|
| Document published | 2026-05-17 |
| Second maintainer identified | Not yet |
| CODEOWNERS updated to dual ownership | Pending second-maintainer onboarding |
| Branch protection PR-review requirement enabled | Pending second-maintainer Phase 3 |
When the first three rows flip to "Done", update this section to record the timeline.
Related material¶
.github/CODEOWNERS— current ownership mappingCONTRIBUTING.md— contributor flow- ADR-0024 — Two-tier test layout — example of a recent ADR shape
- Runbooks — operational competence checklist for §1 criterion