diff --git a/.github/workflows/cli-branch-guard.yml b/.github/workflows/cli-branch-guard.yml new file mode 100644 index 00000000..6f340b03 --- /dev/null +++ b/.github/workflows/cli-branch-guard.yml @@ -0,0 +1,29 @@ +name: CLI Branch Guard + +on: + pull_request: + branches: [main] + +jobs: + check-cli-changes: + runs-on: ubuntu-latest + steps: + - name: Check for CLI changes + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Fail if cli/ changed + run: | + CHANGED_FILES=$(git diff --name-only origin/main...HEAD -- cli/) + if [ -n "$CHANGED_FILES" ]; then + echo "::error::Direct PRs to main cannot include cli/ changes." + echo "::error::CLI changes must go through the 'next' branch first." + echo "" + echo "Changed files in cli/:" + echo "$CHANGED_FILES" + echo "" + echo "To fix: target your PR to 'next' instead of 'main'" + exit 1 + fi + echo "No cli/ changes detected. PR can proceed." diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index dde5ecfc..1f379cb3 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -1,16 +1,15 @@ @@ -160,6 +160,21 @@ parse → validate → execute **Rationale**: We don't want command-specific bugs. We don't want to maintain multiple code paths. Keep it simple. Keep it DRY. No one wants wet code. +### VIII. Fail Fast + +Code MUST fail immediately and loudly when something is wrong. Silent failures and deferred errors are bugs. + +- Use assertions liberally. If a condition should never happen, assert it. +- Validate inputs at the boundary. Reject garbage immediately, don't propagate it. +- Panic on impossible states rather than returning meaningless defaults. +- Error messages MUST be specific: what failed, why, and where. +- NO defensive coding that papers over bugs. If caller passes nil, panic. Don't check and silently return. +- NO "graceful degradation" that hides broken behavior. If it's broken, STOP. +- Prefer hard crashes over corrupted state. A crash is debuggable. Corrupted data is a nightmare. +- Tests MUST assert behavior, not just "run without error". A test that doesn't assert is not a test. + +**Rationale**: The earlier you find a bug, the cheaper it is to fix. Assertions and hard failures surface bugs at development time, not in production. Whimsy code that "handles" errors by ignoring them creates debugging nightmares. Fail hard, fail fast, fix it now. + ## Quality Standards ### Testing Requirements @@ -230,4 +245,4 @@ All contributions MUST comply with these principles. - Complexity MUST be justified in PR descriptions - Principle violations require explicit exemption with documented rationale -**Version**: 1.3.1 | **Ratified**: 2025-10-14 | **Last Amended**: 2026-01-03 +**Version**: 1.4.0 | **Ratified**: 2025-10-14 | **Last Amended**: 2026-01-04 diff --git a/cli/scripts/gendocs.go b/cli/scripts/gendocs.go index 39bd2048..560d19fe 100644 --- a/cli/scripts/gendocs.go +++ b/cli/scripts/gendocs.go @@ -12,11 +12,17 @@ import ( ) func main() { - outputDir := "./web/app/_docs" + outputDir := "../apps/web/app/_docs" if len(os.Args) > 1 { outputDir = os.Args[1] } + // Verify the parent path exists (apps/web/app) - fail fast if structure is wrong + parentDir := strings.TrimSuffix(outputDir, "/_docs") + if _, err := os.Stat(parentDir); os.IsNotExist(err) { + log.Fatalf("web app not found at %s - did the directory structure change?", parentDir) + } + if err := os.MkdirAll(outputDir, 0755); err != nil { log.Fatalf("failed to create output dir: %v", err) }