Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/hooks/pre-push
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# Pre-push gate: mirrors CI checks to catch failures before they reach GitHub
# Runs: branch protection → untracked-file check → dotnet format → Release build → unit/architecture tests → integration tests
# NOTE: git provides refspecs on stdin; interactive prompts must use /dev/tty.
# ⚠️ BYPASS POLICY: git push --no-verify is PROHIBITED without prior written
# approval from Ralph + Aragorn documented in a GitHub issue comment.
set -uo pipefail

ROOT="$(git rev-parse --show-toplevel)"
Expand Down Expand Up @@ -152,6 +154,7 @@ fi
# Requires Docker daemon for Testcontainers-backed dependencies.
INTEGRATION_PROJECTS=(
"tests/Web.Tests.Integration/Web.Tests.Integration.csproj"
"tests/AppHost.Tests/AppHost.Tests.csproj"
)

echo -e "\n${CYAN}🐋 Checking Docker availability for integration tests...${RESET}"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/add-issues-to-project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ permissions:
repository-projects: write

env:
PROJECT_ID: PVT_kwHOA5k0b84BVFTy
STATUS_FIELD_ID: PVTSSF_lAHOA5k0b84BVFTyzhQjgPk
PROJECT_ID: PVT_kwHOA5k0b84BXZpa
STATUS_FIELD_ID: PVTSSF_lAHOA5k0b84BXZpazhSmuGY
BACKLOG_OPTION_ID: f75ad846

Comment on lines 11 to 15
jobs:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/project-board-audit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ permissions:
repository-projects: read

env:
PROJECT_ID: PVT_kwHOA5k0b84BVFTy
PROJECT_ID: PVT_kwHOA5k0b84BXZpa

jobs:
audit:
Expand All @@ -21,7 +21,7 @@ jobs:
- name: Audit project board status drift
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-token: ${{ secrets.GH_PROJECT_TOKEN }}
script: |
const getAllProjectItems = async () => {
const items = [];
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/project-board-automation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ permissions:
repository-projects: write

env:
PROJECT_ID: PVT_kwHOA5k0b84BVFTy
STATUS_FIELD_ID: PVTSSF_lAHOA5k0b84BVFTyzhQjgPk
PROJECT_ID: PVT_kwHOA5k0b84BXZpa
STATUS_FIELD_ID: PVTSSF_lAHOA5k0b84BXZpazhSmuGY
IN_SPRINT_OPTION_ID: 61e4505c
IN_REVIEW_OPTION_ID: df73e18b
DONE_OPTION_ID: "98236657"
Expand All @@ -28,7 +28,7 @@ jobs:
- name: Move linked issues on project board
uses: actions/github-script@v9
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
github-token: ${{ secrets.GH_PROJECT_TOKEN }}
script: |
Comment on lines 28 to 32
const action = context.payload.action;
const pr = context.payload.pull_request;
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/squad-mark-released.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ permissions:
contents: read

env:
PROJECT_ID: PVT_kwHOA5k0b84BVFTy
STATUS_FIELD_ID: PVTSSF_lAHOA5k0b84BVFTyzhQjgPk
PROJECT_ID: PVT_kwHOA5k0b84BXZpa
STATUS_FIELD_ID: PVTSSF_lAHOA5k0b84BXZpazhSmuGY
DONE_OPTION_ID: "98236657"
RELEASED_OPTION_ID: "8e246b27"

Expand Down
35 changes: 35 additions & 0 deletions .squad/agents/boromir/history.md
Original file line number Diff line number Diff line change
Expand Up @@ -1390,3 +1390,38 @@ Decision #26: Lint Workflow Pattern for MyBlog (merged into `.squad/decisions.md
- Review PR #288 for approval
- Merge to `dev` branch
- Workflows become active on next push/PR to dev, insider, or main

## Learnings

### Issue #299 — Pre-Push Gate: AppHost.Tests Was Missing from Live Hook (2026-05-11)

**Root cause:** The playbook and SKILL.md documented `AppHost.Tests` as mandatory in Gate 5, but the live `.github/hooks/pre-push` `INTEGRATION_PROJECTS` array only contained `Web.Tests.Integration`. The hook and docs were out of sync.

**Changes made:**

- `.github/hooks/pre-push` — Added `AppHost.Tests` to `INTEGRATION_PROJECTS` (Gate 5)
- `.squad/playbooks/pre-push-process.md` — Removed 4 non-existent test projects from Gate 4/5 lists; fixed `IssueTrackerApp.slnx` → `MyBlog.slnx`; corrected counts in the gate table
- `scripts/install-hooks.sh` — Corrected Gate 4/5 descriptions in echo summary; replaced informal `--no-verify` tip with the policy statement

**Learnings:**

- Hook arrays and playbook project lists are a dual-maintenance surface. Any future test project addition must land in BOTH the hook array and the playbook simultaneously.
- Use this quick alignment check: `grep -r "csproj" .github/hooks/pre-push .squad/playbooks/pre-push-process.md scripts/install-hooks.sh`
- Stale playbook content (6 test projects, Azurite-backed projects) can mislead agents into believing gates exist that don't — documentation debt has real enforcement consequences.
- The `--no-verify` policy must be stated consistently across all three surfaces (hook comment, playbook, install script); having a permissive "skip in an emergency" line in `install-hooks.sh` while the playbook hard-blocks it created a contradiction.

### Issue #299 — Round 3: docs/CONTRIBUTING.md alignment + bypass policy doc (2026-05-11)

**Context:** Ralph's work-check cycle Round 3 confirmed the source hook and installed hook were already in sync. This round focused on the remaining documentation drift.

**Changes made (PR squad/299-prepush-gate-alignment):**

- `.github/hooks/pre-push` — Added `⚠️ BYPASS POLICY` comment at header (prohibits --no-verify without approval)
- `docs/CONTRIBUTING.md` — Corrected gate table from 5→6 gates; fixed Gate 3 as "dotnet format", Gate 3 as "Release build", Gate 4 as unit tests (4 projects), Gate 5 as integration tests (2 projects); removed references to non-existent `tests/Unit.Tests` and `tests/Integration.Tests`; updated bypass policy language
- `scripts/install-hooks.sh` — Already had correct descriptions from prior round
- `.squad/playbooks/pre-push-process.md` — Already had correct content from prior round

**Learnings:**

- `docs/CONTRIBUTING.md` is a third maintenance surface beyond the hook and playbook. All three must be checked on hook changes.
- Gate numbering in docs must match the hook source exactly (0–5, not 0–4).
42 changes: 42 additions & 0 deletions .squad/decisions/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2331,3 +2331,45 @@ The workflow's "Commit updated README" step now uses `git push origin HEAD:dev`
- **Option B (PR from workflow):** Have the workflow open a PR to main. Rejected — requires `pull-requests: write`, adds noise, and needs the "Build Solution" check to pass before merge.
- **Option C (push to dev) ← CHOSEN:** Simple one-line fix, no new permissions.

---

## Project Board Automation Repair (2026-05-11)

### Context

Project board automation was completely broken. Four workflows (`add-issues-to-project`, `project-board-automation`, `project-board-audit`, `squad-mark-released`) referenced a non-existent project ID (`PVT_kwHOA5k0b84BVFTy`), causing all sync operations to fail silently.

### Decision

Create new project board (MyBlog Project Board, https://github.com/users/mpaulosky/projects/5) and update workflows with correct IDs and authentication.
Comment on lines +2336 to +2344

### Technical Changes

1. **New Project Board:** PVT_kwHOA5k0b84BXZpa (Status Field: PVTSSF_lAHOA5k0b84BXZpazhSmuGY)
2. **GH_PROJECT_TOKEN Secret:** Created with 'project' scope (required for user-owned projects)
3. **Workflow Updates:**
- All four workflows now use new PROJECT_ID and GH_PROJECT_TOKEN
- All workflows updated to use `secrets.GH_PROJECT_TOKEN` (not fallback to GITHUB_TOKEN)
Comment on lines +2351 to +2352

### Known Issue

GitHub Actions caches workflow definitions on the `dev` branch. ENV variable updates committed to git are not picked up by workflow runs. **Workaround:** Test workflows on a fresh branch to bypass cache.

### Next Steps

1. Configure project board field options via UI (Backlog, In Sprint, In Review, Done, Released)
2. Update workflow option IDs once obtained from project board
3. Test workflows on fresh branch: `test-project-board-fix`
4. Verify: Create [Sprint X] issue → should appear on board with correct status

### Rationale

- **Why new project?** Old project ID was invalid and inaccessible
- **Why GH_PROJECT_TOKEN?** User-owned projects require 'project' scope; GITHUB_TOKEN lacks this
- **Why auth escalation OK?** Token only has 'project' scope (no code access), no service escalation

### Alternatives Considered

- **Option A (Fix existing project):** Cannot — old project doesn't exist and can't be recovered
- **Option B (Organization project):** Rejected — adds complexity, requires org changes
- **Option C (User project + GH_PROJECT_TOKEN) ← CHOSEN:** Simpler, already functional
20 changes: 7 additions & 13 deletions .squad/playbooks/pre-push-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,20 +58,18 @@ Before running `git push`, verify:
4. **Release build passes locally** — Gate 3 runs Release (not Debug)

```bash
dotnet build IssueTrackerApp.slnx --configuration Release
dotnet build MyBlog.slnx --configuration Release
```

If build fails, run `.github/prompts/build-repair.prompt.md` to fix.

5. **Unit tests pass** — Gate 4 runs 6 test projects
5. **Unit tests pass** — Gate 4 runs 4 test projects

```bash
dotnet test tests/Architecture.Tests/Architecture.Tests.csproj --configuration Release --no-build
dotnet test tests/Domain.Tests/Domain.Tests.csproj --configuration Release --no-build
dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --configuration Release --no-build
dotnet test tests/Persistence.MongoDb.Tests/Persistence.MongoDb.Tests.csproj --configuration Release --no-build
dotnet test tests/Web.Tests/Web.Tests.csproj --configuration Release --no-build
dotnet test tests/Persistence.AzureStorage.Tests/Persistence.AzureStorage.Tests.csproj --configuration Release --no-build
dotnet test tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj --configuration Release --no-build
```

6. **Docker is running** — Gate 5 requires Docker for integration tests
Expand All @@ -90,30 +88,26 @@ When you execute `git push`, the hook runs automatically:
| **1** | Untracked source files | `.razor`/`.cs` files not staged (prompts y/N) |
| **2** | dotnet format | Any file requires formatting changes (prompts auto-fix y/N) |
| **3** | Release build | `dotnet build --configuration Release` fails (3 attempts) |
| **4** | Unit/Arch/bUnit tests | Any of 6 test projects fail (3 attempts) |
| **5** | Integration tests | Any of 4 integration test projects fail; Docker not running (3 attempts) |
| **4** | Unit/Arch/bUnit tests | Any of 4 test projects fail (3 attempts) |
| **5** | Integration tests | Any of 2 integration test projects fail; Docker not running (3 attempts) |

### Gate 4 — Test Projects (Unit)

```text
tests/Architecture.Tests/Architecture.Tests.csproj
tests/Domain.Tests/Domain.Tests.csproj
tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj
tests/Persistence.MongoDb.Tests/Persistence.MongoDb.Tests.csproj
tests/Web.Tests/Web.Tests.csproj
tests/Persistence.AzureStorage.Tests/Persistence.AzureStorage.Tests.csproj
tests/Web.Tests.Bunit/Web.Tests.Bunit.csproj
```

### Gate 5 — Integration Test Projects (Docker Required)

```text
tests/Persistence.MongoDb.Tests.Integration/Persistence.MongoDb.Tests.Integration.csproj
tests/Web.Tests.Integration/Web.Tests.Integration.csproj
tests/Persistence.AzureStorage.Tests.Integration/Persistence.AzureStorage.Tests.Integration.csproj
tests/AppHost.Tests/AppHost.Tests.csproj
```

These use Testcontainers (mongo:7.0, Azurite) and Aspire DCP. Docker daemon MUST be running.
These use Testcontainers (mongo:7.0) and Aspire DCP (`DistributedApplicationTestingBuilder`). Docker daemon MUST be running.

## Retry Behavior

Expand Down
42 changes: 22 additions & 20 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,18 @@ gate stays current as the repo evolves.
### What the Pre-Push Gate Enforces

The pre-push hook automatically runs before every `git push` and enforces
**5 sequential gates**:
**6 sequential gates**:

| Gate | Rule | Enforced Behavior |
|------|------|--------|
| **0** | Squad branch naming | Rejects pushes on non-`squad/{issue}-{slug}` branches; blocks `main` and `dev` |
| **1** | Untracked source files | Warns if `.razor` or `.cs` files exist but are not staged; prompts to confirm before proceeding |
| **2** | Release build | Runs `dotnet build MyBlog.slnx --configuration Release`; zero warnings or errors required |
| **3** | Unit & architecture tests | Runs `tests/Architecture.Tests` and `tests/Unit.Tests` (Release configuration) |
| **4** | Integration tests | Runs `tests/Integration.Tests` (Release configuration; Docker daemon required) |
| **2** | dotnet format | Runs `dotnet format --verify-no-changes`; prompts to auto-fix if formatting issues found |
| **3** | Release build | Runs `dotnet build MyBlog.slnx --configuration Release`; zero warnings or errors required |
| **4** | Unit & architecture tests | Runs `Architecture.Tests`, `Domain.Tests`, `Web.Tests`, `Web.Tests.Bunit` (Release configuration) |
| **5** | Integration tests | Runs `Web.Tests.Integration`, `AppHost.Tests` (Release configuration; Docker daemon required) |

**Retry logic:** Gates 2–4 allow up to **3 attempts**. Between failures, the
**Retry logic:** Gates 3–5 allow up to **3 attempts**. Between failures, the
hook pauses and prompts you to fix errors, then retries automatically.

### Branch Naming (Strict)
Expand All @@ -55,22 +56,20 @@ pattern, or from `main`/`dev`.

### Bypassing the Gate (Emergency Only)

In rare cases where you need to push despite failures:

```bash
git push --no-verify
```

**Use sparingly.** The hook only blocks clearly broken code. Pushing broken code
to a `squad/*` branch still allows CI to catch it and blocks merge to `dev`, but
wasting CI cycles is not ideal. Fix locally first.
⚠️ **PROHIBITED without prior written approval** from Ralph + Aragorn documented
in a GitHub issue comment. The hook blocks clearly broken code; skipping it
wastes CI cycles and bypasses team quality gates. Fix locally first.

## Development Workflow

### Prerequisites

- **.NET 10 SDK** — [Download](https://dotnet.microsoft.com/en-us/download)
- **Docker daemon** — Required for `tests/Integration.Tests` (Gates 4)
- **Docker daemon** — Required for integration tests (Gate 5: `tests/Web.Tests.Integration`, `tests/AppHost.Tests`)
- **Auth0 account** — See [AUTH0_SETUP.md](AUTH0_SETUP.md) for Auth0 configuration

### Building and Testing Locally
Expand All @@ -79,10 +78,10 @@ wasting CI cycles is not ideal. Fix locally first.
# Restore dependencies
dotnet restore MyBlog.slnx

# Build the solution (Release config, as Gate 2 does)
# Build the solution (Release config, as Gate 3 does)
dotnet build MyBlog.slnx --configuration Release

# Run all tests (as Gates 3 and 4 do)
# Run all tests (as Gates 4 and 5 do)
dotnet test MyBlog.slnx --configuration Release

# Run the application (via Aspire AppHost)
Expand Down Expand Up @@ -123,11 +122,14 @@ git symbolic-ref --short HEAD
2. Run the pre-push gates locally to catch errors early:

```bash
# The hook runs automatically on git push, but you can test manually:
dotnet build MyBlog.slnx --configuration Release
# Run unit/architecture tests (Gate 4)
dotnet test tests/Architecture.Tests --configuration Release --no-build
dotnet test tests/Unit.Tests --configuration Release --no-build
dotnet test tests/Integration.Tests --configuration Release --no-build # requires Docker
dotnet test tests/Domain.Tests --configuration Release --no-build
dotnet test tests/Web.Tests --configuration Release --no-build
dotnet test tests/Web.Tests.Bunit --configuration Release --no-build
# Run integration tests (Gate 5 — requires Docker)
dotnet test tests/Web.Tests.Integration --configuration Release --no-build
dotnet test tests/AppHost.Tests --configuration Release --no-build
```

1. Push:
Expand Down Expand Up @@ -235,22 +237,22 @@ on a new issue branch keeps the repository clean and your work tracking obvious.

## Troubleshooting

### Build Failures (Gate 2)
### Build Failures (Gate 3)

- **Warnings treated as errors:** The Release config enforces
`TreatWarningsAsErrors=true`. Fix warnings first.
- **Missing file references:** Stage any new `.razor` or `.cs` files with
`git add`, then retry.
- **NuGet restore failure:** Run `dotnet restore` manually and retry.

### Test Failures (Gates 3 & 4)
### Test Failures (Gates 4 & 5)

- **Architecture test failure:** Check naming conventions (commands →
`Command`, queries → `Query`, handlers → `Handler`, validators →
`Validator`).
- **DateTime equality failures:** Assert individual fields instead of
whole-record equality; `UtcNow` changes between calls.
- **Docker not running (Gate 4):** Start Docker Desktop and retry.
- **Docker not running (Gate 5):** Start Docker Desktop and retry.
- **Container startup timeout:** Increase Docker resources and verify
images are pulled.

Expand Down
7 changes: 4 additions & 3 deletions scripts/install-hooks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ echo " sprint/{N}-{slug} passes Gate 0 and exits (skips feature gates)"
echo " 1. Warns about untracked .razor/.cs source files"
echo " 2. dotnet format --verify-no-changes (formatting check; offers auto-fix)"
echo " 3. Release build (dotnet build MyBlog.slnx --configuration Release)"
echo " 4. Unit/arch tests (tests/Architecture.Tests, tests/Unit.Tests)"
echo " 5. Integration tests (tests/Integration.Tests — Docker required)"
echo " 4. Unit/arch tests (tests/Architecture.Tests, tests/Domain.Tests, tests/Web.Tests, tests/Web.Tests.Bunit)"
echo " 5. Integration tests (tests/Web.Tests.Integration, tests/AppHost.Tests — Docker required)"
echo ""
echo "To skip in an emergency: git commit --no-verify / git push --no-verify"
echo "⚠️ git push --no-verify is prohibited without prior written approval from Ralph + Aragorn."
echo " Document the bypass in a GitHub issue comment before using --no-verify."
Loading