BOHICA-LABS / BOHICA-LABS/vsdd-factory

feat(preflight): factory should validate branching strategy, default branch, branch protection, and GitHub repo settings on session entry

Open
#227 2 comments 0 reactions 0 assignees View on GitHub
Dominant language
Rust
Stars
2
Forks
1
Avg merge
6h 43m
Merged PRs (30d)
29

Description

## Summary

`/vsdd-factory:setup-env` and `/vsdd-factory:factory-health` are the two skills an operator is told to run at session start. **Neither validates the repository's branching strategy, default branch, branch protection rules, or any GitHub-side repo configuration.** The factory will happily run any pipeline phase against a repo that has:
- No default branch set (or `main` when `develop` is the actual integration branch)
- No branch protection on the integration branch
- A branching strategy that conflicts with the factory's hardcoded `develop` assumption
- Settings/policies (squash-merge only, signed-commit enforcement, CODEOWNERS, Dependabot, etc.) that contradict pipeline assumptions

The closest the factory gets is:
- `scaffold-claude-md` **detects** branch strategy when generating a CLAUDE.md — purely textual, no enforcement, opt-in.
- `repo-initialization` **creates** a new repo with `develop` as default + branch protection — only when greenfield.
- `brownfield-sequence` S2 mentions "Verify default branch is set / Check/add branch protection if missing" — textual; the grep for an actual implementation step turns up nothing concrete.

For an existing repo (brownfield, or just "session start"), nothing checks any of this.

## What's missing — concrete checks needed

### Branching-strategy preflight (per-session)

```bash
# 1. Default branch
default_branch=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|origin/||')
test -n "$default_branch" || warn "origin/HEAD unset — default branch not configured"

# 2. Detect strategy
has_develop=$(git ls-remote --heads origin develop)
has_release=$(git ls-remote --heads origin 'release/*')
has_main=$(git ls-remote --heads origin main)

if [ -n "$has_develop" ] && [ -n "$has_main" ]; then
strategy=gitflow
elif [ -n "$has_main" ] && [ -z "$has_develop" ]; then
strategy="github-flow (trunk-based)"
fi

# 3. Confirm with the operator on first run; cache in .factory/repo-config.yaml
# Subsequent sessions: re-detect and warn on drift

# 4. Guard against bad starting branch
current=$(git rev-parse --abbrev-ref HEAD)
case "$strategy:$current" in
gitflow:main|gitflow:master)
fail "gitflow in use but you are on $current. Switch to develop before running pipeline operations." ;;
esac
```

### Branch protection preflight

```bash
gh api "repos/$OWNER/$REPO/branches/$default_branch/protection" 2>&1 || \
warn "no branch protection on $default_branch — required reviews, status checks, signed commits all unenforced"
```

Should at minimum surface:
- Are required reviews configured?
- Are status checks required?
- Is `enforce_admins` on?
- Are signed commits required?
- Is `delete_branch_on_merge` set?
- Are linear history / signed merges configured?

### GitHub repo settings sweep

```bash
gh api "repos/$OWNER/$REPO" --jq '{
visibility, default_branch, allow_squash_merge, allow_merge_commit,
allow_rebase_merge, delete_branch_on_merge, has_issues, has_projects,
license: .license.spdx_id, archived, has_security_policy
}'
```

Report missing/unset things that the factory pipeline assumes:
- `delete_branch_on_merge=true` (the pr-manager skill calls `gh pr merge --delete-branch`; if the repo doesn't honor it, branches accumulate)
- License set (compliance — affects whether the holdout-evaluator can ship)
- Signed-commit enforcement matching the operator's local `commit.gpgsign`

### CODEOWNERS / templates / community files

```bash
test -f .github/CODEOWNERS || warn "no CODEOWNERS — pr-manager review dispatch may be brittle"
test -f .github/PULL_REQUEST_TEMPLATE.md || info "no PR template — pr-manager will create one"
test -f SECURITY.md || info "no SECURITY.md — security-reviewer may surface as a finding"
ls .github/workflows/*.yml 2>/dev/null | wc -l | xargs -I{} info "CI workflows: {} files"
```

### Dependency posture preflight

For each detected language, run the language-native CVE/audit tool:

| Language | Tool |
|----------|------|
| Go | `govulncheck ./...` |
| Rust | `cargo audit` |
| Python | `pip-audit` |
| Node | `npm audit --audit-level=high` |

Surface high-severity hits at session start so the operator decides whether to address before kicking off a pipeline that's about to add more.

### Cache the result

Write what was discovered to `.factory/repo-config.yaml` so subsequent sessions don't re-prompt the operator:

```yaml
default_branch: develop
branching_strategy: gitflow
integration_branch: develop
release_branch_pattern: 'release/*'
hotfix_branch_pattern: 'hotfix/*'
feature_branch_pattern: 'feature/*'
discovered: 2026-06-23T19:30:00Z
discovered_by:
```

The brownfield-ingest skill should populate this; setup-env / factory-health should verify it stays consistent across sessions.

## Why this matters

1. **Silent misconfiguration risk.** A team that uses GitHub-flow (trunk-based, `main` only) gets bizarre behavior when the factory creates a `develop` branch they don't want, then PRs to `develop` instead of `main`. The factory doesn't ask; it just assumes.
2. **Branch protection blind spot.** The factory's pipeline assumes signed commits / required checks. If the repo has no protection on `develop` (or the operator is somehow allowed to bypass it), the pipeline silently produces unverified work.
3. **"Don't push to main" is only enforced by the user's CLAUDE.md.** If a team adopts vsdd-factory and the user's CLAUDE.md isn't propagated, nothing stops a `gh pr merge` against `main` directly.
4. **Repo settings drift.** A repo can have settings flipped by an admin between sessions; the factory should detect drift and re-prompt.
5. **No discovery for brownfield.** When ingesting an existing repo, the factory should LEARN the branching strategy from the repo (merge patterns, branch presence, GitHub default), not assume.

## Acceptance criteria

- [ ] A new `/vsdd-factory:repo-preflight` skill (or expansion of `factory-health`) runs at session entry and checks:
- default branch is set on origin
- branching strategy detected (gitflow / github-flow / gitlab-flow / unknown)
- branch protection rules present on the integration branch
- signed-commit enforcement matches operator config
- GitHub repo settings: `delete_branch_on_merge`, `allow_squash_merge`, license, CODEOWNERS, SECURITY.md
- Dependency posture: language-native CVE check (govulncheck / cargo audit / pip-audit / npm audit)
- [ ] On first run, results are cached to `.factory/repo-config.yaml` (committed to factory-artifacts).
- [ ] On subsequent runs, drift from cached state is surfaced as a warning.
- [ ] When gitflow is detected, attempting to start a pipeline phase from `main`/`master` is BLOCKED with a clear error.
- [ ] When trunk-based is detected, the pipeline's `develop` assumption is swapped for the actual integration branch (configurable).
- [ ] `worktree-manage`, `code-delivery`, `pr-manager` all read the integration-branch name from `.factory/repo-config.yaml` instead of hardcoding `develop`.

## Found during

`/vsdd-factory:setup-env` then `/vsdd-factory:factory-health` on `switchboard-blue` (Go project, gitflow, 2026-06-23, vsdd-factory@1.0.0-rc.21). Neither skill validates branching strategy, default branch, or branch protection. The project's gitflow setup is enforced only by the user's CLAUDE.md (which mandates "don't push to main / develop directly, always PR") and the project CLAUDE.md. If those weren't in place, the factory wouldn't catch a wrong-branch operation.

Specific issue earlier in this session: the `/factory-health` orphan-branch recipe runs `git checkout -` which, in a gitflow repo, could happily land on `main` if that's where the session started — no skill would have caught it as a violation of the gitflow contract.

## Related

- (this session, filed) #204 — `git checkout -` strands session on factory-artifacts; the recipe also has no branch-name awareness
- skills/scaffold-claude-md/SKILL.md — has the detection logic (default branch, develop existence, branch naming patterns) but only writes to a doc, doesn't enforce
- skills/repo-initialization/SKILL.md — has the enforcement logic but only on greenfield (new repo creation)
- agents/orchestrator/brownfield-sequence.md S2 — textually mentions "Verify default branch is set" with no implementation

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.