Solo-Developer Governance Profile¶
Use the solo-dev profile for a repository with one accountable maintainer and
no dependable second reviewer. It still requires pull requests, required
status checks, protected main, advisory automated feedback, and an auditable
merge path. It simplifies operations; it does not weaken team profiles or
authorize administrator bypass.
Choose the right profile¶
| Signal | solo-dev |
team-dev |
regulated-team |
|---|---|---|---|
| Active maintainers | One accountable maintainer | Two or more regular contributors | Any team with mandated separation of duties |
| Routine independent review | Not required through size:XL; checks remain required |
Required for medium and higher risk | Required for every risk tier |
| XXL PR intent | One qualified human approval | Independent PR approval | Two independent PR approvals |
| Merge queue | Deferred | Deferred | Required |
| Production deployment | Autonomous after merge and required checks | Protected GitHub environment approval | Protected GitHub environment approval |
| Best fit | True single-owner service with reliable CI | Shared ownership and normal team delivery | Regulated, security-sensitive, or audited delivery |
Choose team-dev as soon as another person regularly owns, reviews, or operates
the repository. Choose regulated-team when policy, customer commitments, or
compliance controls require separation of duties. Repository size is not the
deciding factor; ownership and risk are.
Non-negotiable solo-dev controls¶
minimal branch policy means the minimum protected posture, not an unprotected
branch:
- every change to
mainuses a pull request - direct pushes, force pushes, and branch deletion are blocked
- required status checks run against the current head and must succeed
- administrators are subject to the rules and the bypass list is empty
- the auto-merge workflow uses GitHub auto-merge, never
--admin - XS through XL pull requests require zero independent PR approvals
size:XXLpull requests require one qualified human approval- governance policy, human-boundary, and canonical/distributed/downstream executor changes are always classified as critical
- the executor's
BaseCoat merge eligibilitystatus is required as a fail-closed policy and check aggregator
The six canonical validation checks come from
.github/governance/policy-packs.json:
lint-and-validatetestvalidate-commit-messagesvalidate-unixvalidate-windowsrelease-label-gate
The ruleset must also require BaseCoat merge eligibility. The trusted executor
publishes that commit status directly against the pull request head SHA. Pin the
required context to GitHub Actions integration ID 15368 for provenance.
Copilot review feedback is advisory for solo-dev; it never becomes a merge
blocker or requires conversation resolution. Pending validation, a missing
qualified size:XXL approval, or policy violations keep the status red and
prevent both manual and automatic merge.
If a consumer uses different check names, update its local policy pack and ruleset together. Do not delete checks from only one surface.
Self-merge policy¶
solo-dev PRs through size:XL use zero independent PR approvals. The
accountable maintainer may author and merge after required checks and the
BaseCoat merge eligibility status pass. A size:XXL PR requires one qualified
approval on its current head. This autonomous path applies only to solo-dev;
team and regulated profiles retain their independent-review requirements.
Explicit policy-exception, security-incident, and incident boundaries remain human-controlled. They are exceptions to the normal feature-delivery size policy, not routine review gates.
For linked-issue evidence, adding, editing, or deleting the qualified
/approve comment and adding or removing the approved label automatically
sets linked PR eligibility to pending and dispatches reevaluation. Editing the
PR title or body also reevaluates closing links.
Production release paths are explicit policy, not inferred from the broad
high tier. The shipped policy pack includes every BaseCoat workflow that can
target the production environment in production_release_paths, plus the
trusted deployment job and exact environment scalar in
production_environment.workflow_bindings, plus a SHA-256 digest covering the
entire workflow. For changes to those workflows, the executor reads the PR-head
file and verifies both the full digest and environment binding before checking
the live protected GitHub production environment. This prevents moving
production effects into an unprotected sibling job. PR approval is not treated
as equivalent; in solo-dev, a validated merge can continue through the
environment without an additional deployment-review wait.
Before enabling solo-dev self-merge in a consumer repository, inventory
every local workflow that can deploy to production, including workflows with
dynamic environment expressions, and add every path to
production_release_paths. Add its deployment job ID and exact environment
scalar and normalized UTF-8 SHA-256 digest to
production_environment.workflow_bindings. To change a production workflow,
first merge the policy PR that preauthorizes the exact prospective digest; then
submit the byte-matching workflow PR. This two-PR sequence is mandatory because
the executor reads trusted policy from main.
Do not activate the solo-dev ruleset until the consumer check names and
production workflow inventory are complete.
Risk tier is the highest path tier matched by the changed files. See Governance Policy Packs and Risk-Tier Autonomy Policy.
Configure the profile¶
1. Commit the onboarding contract¶
Create .github/basecoat-onboarding-profile.json:
{
"contract_version": "1.0.0",
"profile": "solo-dev",
"branch_policy": "minimal",
"workflow_pack": "solo",
"template_pack": "solo",
"telemetry_mode": "local",
"secrets_mode": "local",
"hook_pack": "none",
"preserve_local_customizations": true,
"allow_profile_downgrade": false
}
Validate the selection through the existing bootstrap entrypoint:
pwsh scripts/bootstrap.ps1 `
-OnboardingContractPath .github\basecoat-onboarding-profile.json `
-Silent
2. Install the existing merge executor¶
Use the downstream workflow installer rather than writing another auto-merge implementation:
pwsh scripts/configure-downstream-workflows.ps1 `
-Workflow pr-auto-merge-executor.yml `
-KeepUnknownBc
This installs:
.github/workflows/basecoat-pr-auto-merge-executor.yml.github/governance/policy-packs.json.github/governance/human-approval-boundaries.json
To use the approved-spec directive, also install the issue approval workflow:
That workflow validates the supplied spec reference, applies the
repository-standard approved label for a qualified /approve or
/spec-2-prod directive, assigns the coding agent, and dispatches
merge-eligibility reevaluation after the label is finalized.
The exact -Workflow selector installs only the executor and its governance
contracts. Targeted mode preserves every non-selected workflow; -KeepUnknownBc
adds an explicit safeguard for older installer versions. On reinstall, targeted
mode also preserves existing consumer governance files so local check names and
production paths are not overwritten. Review upstream governance changes and
merge them into the consumer policy deliberately.
The executor evaluates an existing PR and enables GitHub-native auto-merge with
gh pr merge --auto --squash --delete-branch. It does not discover BaseCoat
updates or create upgrade PRs. Keep consumer update detection and PR creation in
the consumer updater lifecycle; do not duplicate that implementation here.
3. Set repository options¶
In GitHub:
- Settings > General > Pull Requests
- enable Allow auto-merge
- enable Allow squash merging
- enable Automatically delete head branches
- Settings > Actions > General > Workflow permissions
- keep Read repository contents and packages permissions
- do not grant repository-wide write access; the executor declares only its
required
actions: write,checks: read,deployments: read,contents: write,pull-requests: write,issues: write, andstatuses: writepermissions - if the repository installs a workflow that opens pull requests with the
default token (for example
issue-to-spec-synthesis.yml), review the separate Allow GitHub Actions to create and approve pull requests platform policy; see PR-creation permission below before enabling it at any scope - Settings > Rules > Rulesets
- create an active branch ruleset targeting the default branch
- require zero approving reviews
- do not require extra approval for unattributed changes
- do not require conversation resolution; Copilot feedback is advisory
- require all six validation checks plus
BaseCoat merge eligibility - require branches to be up to date
- block deletion and non-fast-forward pushes
- leave the bypass list empty
- Settings > Secrets and variables > Actions > Variables
- set
BASECOAT_POLICY_PACKtosolo-dev - Settings > Environments > production
- do not configure required reviewers for
solo-dev - allow only protected branches or selected deployment branches including
main - keep production secrets and variables environment-scoped
- preserve protected or selected deployment branches including
main
Set the variable explicitly even though workflows can fall back to the policy
file's default_profile. Explicit selection prevents onboarding and workflow
defaults from being confused during migrations.
PR-creation permission for automation workflows¶
Workflows that open pull requests using the default GITHUB_TOKEN (for
example issue-to-spec-synthesis.yml, which turns a triaged issue into a
draft spec PR) are blocked by a separate, hierarchical GitHub setting:
Allow GitHub Actions to create and approve pull requests. It is distinct
from the read/write workflow-permissions radio button above — a workflow's
own permissions: block already grants it pull-requests: write, but
GitHub still refuses the actual create/approve call unless this toggle is on.
The setting is enforced top-down: Enterprise → Organization → Repository.
If an Enterprise or Organization has set it to Disabled (not
Not enforced), no lower level can override it — gh api calls to enable it
at the repo or org return 409 with a message that write permissions for
workflows are disabled by the enterprise, even with admin:org scope.
This policy capability and related Actions settings do not ship with BaseCoat or with repository templates. BaseCoat ships workflow files, policy-pack files, and bootstrap guidance; the Enterprise, organization, and repository owners must separately decide whether Actions may create or approve pull requests in the consumer environment.
First avoid PR-creating automation when a read-only report or issue comment is sufficient. When PR creation is required, use this policy decision order:
- Treat Enterprise-level global enablement as the highest-blast-radius option: it can affect every organization and repository unless each org explicitly restricts it. Do not enable the Enterprise checkbox solely to unblock one BaseCoat or downstream repository unless org restriction, audit ownership, and rollback are already part of the plan.
- Prefer an organization-level override or restriction when the Enterprise owner can delegate safely but the repository-level control is unavailable.
- Prefer repository-level opt-in only when GitHub exposes that control under the current Enterprise and organization policy, and only for repositories that install a PR-creating workflow.
- If the platform policy cannot be narrowed safely, use a scoped
GH_AW_GITHUB_TOKENonly as a temporary fallback. A GitHub App or brokered token with repository-scoped installation and auditability is the preferred durable direction.
The blast radius matters more in solo-dev: routine XS through XL pull
requests have required_approving_review_count: 0. A broadly enabled toggle
allows every workflow in scope to create PRs and submit approving PR reviews,
not just the intended workflow. In a permissive ruleset, an unrelated or
compromised workflow could create and self-approve a PR that then has no
independent-review gate. Keep enablement scoped to the repositories that need
it, and confirm the workflow itself has compensating controls such as draft-only
output and an explicit human /approve directive before implementation.
If an Enterprise policy is Disabled with no delegation, no repo or org action
can fix it locally. Escalate to an Enterprise owner for policy review instead
of treating it as a repository-admin prerequisite. Falling back to a
manually-created PAT (see docs/operations/github-secrets.md) trades an
enforced platform policy for a standing secret with rotation burden; treat it
strictly as a temporary exception while a GitHub App or brokered token design is
implemented.
scripts/bootstrap.ps1 performs a best-effort, warn-only check for this
setting when a PR-creating workflow is present, so onboarding surfaces the
requirement without blocking on it.
Programmatic GitHub setup¶
The caller needs repository administration permission. Replace
OWNER/REPOSITORY before running these commands.
$repository = "OWNER/REPOSITORY"
gh api --method PATCH "repos/$repository" `
-F allow_auto_merge=true `
-F allow_squash_merge=true `
-F delete_branch_on_merge=true
gh variable set BASECOAT_POLICY_PACK `
--repo $repository `
--body "solo-dev"
# Only if the repository installs a PR-creating automation workflow
# (for example issue-to-spec-synthesis.yml), and only after reviewing
# Enterprise, organization, and repository policy scope. A 409 here means the
# setting is blocked above the repository — escalate to that owner instead of
# retrying at the repository scope.
gh api --method PUT "repos/$repository/actions/permissions/workflow" `
-F can_approve_pull_request_reviews=true
Create solo-dev-main-ruleset.json:
{
"name": "BaseCoat solo-dev main",
"target": "branch",
"enforcement": "active",
"bypass_actors": [],
"conditions": {
"ref_name": {
"include": ["~DEFAULT_BRANCH"],
"exclude": []
}
},
"rules": [
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "pull_request",
"parameters": {
"allowed_merge_methods": ["squash"],
"dismiss_stale_reviews_on_push": true,
"require_code_owner_review": false,
"require_extra_approval_for_unattributed_changes": false,
"require_last_push_approval": false,
"required_approving_review_count": 0,
"required_review_thread_resolution": false
}
},
{
"type": "required_status_checks",
"parameters": {
"do_not_enforce_on_create": false,
"strict_required_status_checks_policy": true,
"required_status_checks": [
{"context": "lint-and-validate", "integration_id": 15368},
{"context": "test", "integration_id": 15368},
{"context": "validate-commit-messages", "integration_id": 15368},
{"context": "validate-unix", "integration_id": 15368},
{"context": "validate-windows", "integration_id": 15368},
{"context": "release-label-gate", "integration_id": 15368},
{"context": "BaseCoat merge eligibility", "integration_id": 15368}
]
}
}
]
}
Apply it:
For an existing ruleset, use PUT repos/$repository/rulesets/$rulesetId with
the same payload rather than creating a duplicate.
Verify before enabling routine auto-merge¶
Run each required workflow at least once so GitHub knows its check context, then inspect the live configuration:
gh variable get BASECOAT_POLICY_PACK --repo $repository
gh api "repos/$repository" `
--jq '{allow_auto_merge,allow_squash_merge,delete_branch_on_merge}'
gh api "repos/$repository/rulesets" `
--jq '.[] | select(.name == "BaseCoat solo-dev main") | {id,enforcement,bypass_actors}'
gh workflow list --repo $repository
Open a non-draft test PR and confirm:
- an XS through XL PR reports zero required approvals and becomes eligible after required checks pass; Copilot review feedback remains advisory
- auto-merge remains pending while any required check is missing or failing
- an XXL test PR remains blocked until a qualified reviewer approves its current head
- a linked issue with a supplied spec reference and qualified
/approveor/spec-2-proddirective assigns the coding agent - the coding agent opens a ready implementation PR with a closing reference to the approved issue
- production deployment continues after a validated merge without a separate environment-review wait
- no run or command uses an administrator bypass
Rollback¶
Use normal Git history for content rollback:
- disable the auto-merge executor workflow
- disable repository auto-merge if automated landing must stop immediately
- revert the merged PR in a new protected PR
- restore the previous ruleset payload if protection configuration changed
- rerun required checks and record the rollback PR or incident
Do not force-push main, delete audit history, or use gh pr merge --admin.
A failed critical or production change follows the risk-tier rollback and
incident process rather than a solo-dev exception.
Transition to a stronger profile¶
Move to team-dev¶
Transition when a second maintainer becomes active, ownership is shared, or routine independent review is expected:
- change the onboarding contract to
profile: "team-dev"andmigration_from: "solo-dev" - set
BASECOAT_POLICY_PACK=team-dev - update the ruleset to require the policy-pack approval baseline
- rerun bootstrap and downstream workflow installation
- verify required checks and reviewer routing before merging new work
Move to regulated-team¶
Transition when compliance, security, or customer controls require stricter evidence or separation of duties:
- migrate from the current profile to
regulated-team - require the regulated approval counts and
prd-spec-gate - enable the required merge queue and organization-managed secrets/telemetry
- remove any local exceptions and verify the empty bypass posture
When migration_from records the currently applied profile, bootstrap compares
profile strength and blocks a weaker target unless explicitly approved with
allow_profile_downgrade: true. Omitting migration_from prevents bootstrap
from identifying the transition, so migration contracts must always include it.
See the
Onboarding Profile Contract
for migration semantics.
Related contracts¶
- Onboarding Profile Contract
- Governance Policy Packs
- Main Branch Protection Policy
- Risk-Tier Autonomy Policy
- Workflow Installation
.github/base-coat/workflows/pr-auto-merge-executor.yml