From 2810d93b894cde51f3d36e1f3fef631be4a24e1f Mon Sep 17 00:00:00 2001 From: Micheal Wilkinson Date: Sat, 21 Mar 2026 12:40:31 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20add=20copilot=20instructions=20for=20?= =?UTF-8?q?=C3=86ther=20Go=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/copilot-instructions.md | 366 ++++++++++++++++++++++++++++++++ 1 file changed, 366 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..c6a9e2c --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,366 @@ +# Æther Go Project Workflow Instructions + +These instructions apply to all coding work in Æther Go repositories. + +This file is self-contained. Do not assume access to this Guides repository or any other Æther repository when following these instructions. + +## Engineering Process: Strict TDD + +Follow TDD (Red, Green, Refactor) for all feature and bug-fix work: + +- **Red**: Write or update a test first. Confirm the test fails for the expected reason. +- **Green**: Implement the minimum code needed to pass the test. +- **Refactor**: Improve code only after tests are green. + +Do not implement behavior before a failing test exists, unless the user explicitly asks to skip TDD. + +## Testing Requirements + +- Always create and update tests in `*_test.go` files (Go language standard). +- Use the repository's established test framework (prefer testify suites for new coverage where appropriate). +- Run focused tests for touched packages first using `go test -run ./path/to/package`. +- Run broader package or module suites as needed. +- Run full project validation when requested or when change risk warrants it. +- Preserve behavior validated by the repository's behavior/integration suites unless a behavioral change is explicitly requested. + +## Coverage Standards + +Apply coverage gates per package/module, not repository-wide aggregate: + +- **Target**: 80%+ coverage per module. +- **Warning zone**: 65% to 79.99% (below target, improve during normal engineering work). +- **High risk**: 50% to 64.99% (requires explicit justification and follow-up). +- **Fail gate**: Below 50% (unacceptable). + +Exclude generated code from coverage calculations. Run coverage analysis for changed modules: + +```bash +go test -covermode=atomic -coverprofile=coverage.out ./... +``` + +## Commit Checkpoints + +Create commits at these checkpoints unless the user explicitly asks not to commit: + +- After writing failing tests (red commit). +- After implementation passes tests (green commit). +- After refactoring (if substantial refactoring occurred). +- After changelog updates (separate docs-only commit). + +For each functional change block, create at least one code commit before moving to unrelated work. Keep commits small and scoped to one change unit. Use non-interactive git commands. + +### Changelog Commits + +After completing a change block: + +1. Update the changelog (typically `CHANGELOG.md`). +2. Create a separate docs-only commit for changelog updates. +3. Keep changelog commits scoped to documentation changes only—do not mix code edits into that commit. + +### Commit Message Guidance + +Prefer conventional commits with clear scopes and concise summaries. + +- Preferred format for Go maintenance and tooling changes: `chore(go): ` +- Preferred format for documentation updates: `docs: ` +- Keep summaries lowercase, imperative, and under 72 characters when possible. +- Use one commit per logical change. + +Examples: + +- `chore(go): update dependency injection guidance` +- `docs: clarify security scanning requirements` + +## Go Conventions + +- **Go version**: Target the repository's standard Go toolchain (typically `1.26.1`) and maintain compatibility with declared repository settings. +- **Test files**: Keep tests in `*_test.go` files in the same package as the code being tested. +- **Test suites**: Prefer testify suites for new Go test coverage where appropriate. +- **Focused testing**: Run package-specific tests first, then broader validation when requested. +- **Behavior parity**: Use the repository behavior suite for behavior parity validation when relevant. + +## Dependency Injection (DI) Pattern + +Dependency Injection is a required architectural pattern in Æther Go projects. Use the standards below directly: + +- **Interfaces as contracts**: Define interfaces to represent dependencies; place interfaces in the same package as consumers. +- **Concrete structs**: Implement concrete types as structs that satisfy interfaces. +- **Constructor functions**: Use `New` constructor functions to wire dependencies and validate inputs. +- **No globals or singletons**: Accept dependencies as parameters; avoid hidden global state. +- **Testing with DI**: Create concrete mock implementations of interfaces for testing; avoid reflection-based mocking. + +Example pattern: + +```go +// Interface defines a contract. +type UserRepository interface { + GetUser(ctx context.Context, id string) (*User, error) +} + +// Concrete implementation satisfies the interface. +type PostgresUserRepository struct { + db *sql.DB +} + +func (r *PostgresUserRepository) GetUser(ctx context.Context, id string) (*User, error) { + // Implementation +} + +// Constructor function injects dependencies. +func NewPostgresUserRepository(db *sql.DB) (*PostgresUserRepository, error) { + if db == nil { + return nil, errors.New("database connection cannot be nil") + } + return &PostgresUserRepository{db: db}, nil +} + +// Consumer type accepts interface dependency. +type UserService struct { + repo UserRepository +} + +func NewUserService(repo UserRepository) (*UserService, error) { + if repo == nil { + return nil, errors.New("UserRepository cannot be nil") + } + return &UserService{repo: repo}, nil +} +``` + +Key rules: + +- Validate injected dependencies in constructors; return error if validation fails. +- Keep interfaces minimal and focused on the consumer's contract (Interface Segregation Principle). +- Organize packages by domain, not by layer (avoid `service`, `handler`, `repository` top-level packages). +- Break circular dependencies using interfaces; define the interface in the consuming package. + +## Additional Go Conventions + +Beyond dependency injection, follow these Go-specific conventions: + +### Error Handling + +- Return errors as the last return value. +- Use `errors.New()` or `fmt.Errorf()` for simple errors; use custom error types for complex cases. +- Wrap errors with context using `fmt.Errorf("%w", err)` in Go 1.13+. +- Do not log and return errors; let the caller decide how to handle. + +Example: + +```go +func (s *UserService) GetUser(ctx context.Context, id string) (*User, error) { + if id == "" { + return nil, fmt.Errorf("GetUser: invalid user id") + } + user, err := s.repo.GetUser(ctx, id) + if err != nil { + return nil, fmt.Errorf("GetUser: %w", err) + } + return user, nil +} +``` + +### Package Organization + +- Use domain-driven design principles; packages represent domain entities or use cases. +- Keep package dependencies acyclic; use interfaces to break circular dependencies. +- Place interfaces in consumer packages, not separate `interfaces` packages. +- Consider a `cmd/` directory for application entry points and `internal/` for domain logic. + +Example structure: + +``` +myapp/ + cmd/ + myapp/ + main.go # Dependency wiring + internal/ + user/ + user.go # Domain model + service.go # UserService and UserRepository interface + repository.go # PostgresUserRepository + auth/ + auth.go # Domain model + service.go # AuthService and AuthProvider interface +``` + +### Concurrency Patterns + +- Use goroutines and channels for concurrent work. +- Prefer message passing (channels) over shared memory. +- Use `context.Context` for cancellation and timeouts. +- Protect shared state with mutexes only when channels are not practical. + +Example: + +```go +func (s *UserService) ProcessUsers(ctx context.Context, ids []string) error { + workers := 5 + jobs := make(chan string, len(ids)) + errors := make(chan error, len(ids)) + + for i := 0; i < workers; i++ { + go func() { + for id := range jobs { + if err := s.processUser(ctx, id); err != nil { + errors <- err + } + } + }() + } + + for _, id := range ids { + jobs <- id + } + close(jobs) + + for i := 0; i < len(ids); i++ { + select { + case err := <-errors: + if err != nil { + return err + } + case <-ctx.Done(): + return ctx.Err() + } + } + return nil +} +``` + +### Code Organization and Naming + +- Use clear, descriptive names for types and functions. +- Avoid `util`, `helper`, or `common` packages; prefer domain-specific package names. +- Keep files focused; one struct per file is reasonable if the struct is substantial. +- Use `context.Context` as the first parameter in functions that can block or make external calls. + +## Workflow and Release Standards + +When updating CI workflows or release logic: + +- Use the repository's standard Go setup (typically `actions/setup-go@v5` with pinned version and go.sum caching). +- Keep workflow summary output using the summary-file pattern: + - Define `SUMMARY_FILE` environment variable per job. + - Append markdown output from steps to the summary file. + - Print the summary in a final `Summary` step with `if: ${{ always() }}` condition. +- Badge URLs must use `https://{HOST}/{OWNER}/{REPO}/actions/workflows/{WORKFLOW_FILE}.yml/badge.svg{?CONTEXT_PARAMS}`. +- Badge-link targets must use `https://{HOST}/{OWNER}/{REPO}/actions/runs/latest?workflow={WORKFLOW_FILE}.yml{&CONTEXT_PARAMS}`. +- `CONTEXT_PARAMS` is optional; available params are `branch`, `event`, `style` for badge URLs and `branch`, `event` for badge-link targets. Prefer `branch` and `event` when filtering run context; if `style` is used, place it last. +- Prefer latest-run pages for badge links for fast status triage. + +### Composite Actions and Release Orchestration + +Use `https://git.hrafn.xyz/aether/vociferate` as the default release-management tool when integrating Æther composite actions: + +- Pin all action references to released tags (for example `@v1.0.0`). +- Keep all vociferate references on the same tag within a workflow. +- In self-hosted runner environments (git.hrafn.xyz), use explicit `https://` action paths in `uses:` references and avoid shorthand owner/repo coordinates. +- Use `prepare` action to update changelog/version and create release tags. +- Use `publish` action to create/update release notes and assets from existing tags. +- Do not mix alternate release actions unless a repository-local policy explicitly documents an override. +- Use `coverage-badge` action after tests produce `coverage.out` for coverage artifact uploads. +- For pre-conditions: checkout with full history (`fetch-depth: 0`), valid credentials, and required bucket variables/secrets. + +Minimal standalone release workflow example: + +```yaml +name: release + +on: + push: + branches: [main] + +permissions: + contents: write + +jobs: + prepare: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Vociferate prepare + uses: https://git.hrafn.xyz/aether/vociferate/prepare@v1.0.0 + + publish: + needs: prepare + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Vociferate publish + uses: https://git.hrafn.xyz/aether/vociferate/publish@v1.0.0 +``` + +## Project Conventions + +- **Automation**: Prefer `justfile` for task automation; mirror core CI operations locally. +- **Dependency management**: Use `go.mod` and `go.sum` for version tracking. +- **Structure**: Keep code organized in logical packages; avoid deep nesting. + +## Security Standards + +Security standards (self-contained): + +- **gosec**: Run static security analysis for Go code. + - Command: `gosec ./...` + - Purpose: Detect common security issues (hard-coded secrets, SQL injection, weak crypto, etc.) + - Suppress: Use `#nosec` comments only with documented justification. + +- **govulncheck**: Check Go code and dependencies for known vulnerabilities. + - Command: `govulncheck ./...` + - Purpose: Detect vulnerabilities in direct and transitive dependencies. + - Address: Update vulnerable dependencies to patched versions. + +- **Dependency hygiene**: Keep `go.mod` and `go.sum` clean; run `go mod tidy` and `go mod verify` regularly. + +Integrate both tools into CI workflows; fail builds on high/critical findings. + +## Validation Sequence + +Execute validation in this order (unless repository policy specifies otherwise): + +1. Run focused package tests that directly cover the changed code. +2. Run broader package or module test suites as needed. +3. Run `gosec ./...` for security analysis. +4. Run `govulncheck ./...` for vulnerability scanning. +5. Run full project or behavior/integration suites when change scope or risk warrants it. +6. Verify coverage gates per changed module/class (target 80%, low bound 65%, fail below 50%). + +## Safety and Scope + +- Do not revert unrelated local changes. +- Avoid broad refactors outside the requested scope. +- Keep implementation minimal and aimed only at passing the failing test. +- Do not add code that the test does not exercise. + +## Disambiguation and Decision-Making + +If blocked by ambiguity: + +- Ask one concise clarifying question rather than guessing. +- Proceed with the most reasonable interpretation based on context. +- Document any assumption in a commit message or code comment if relevant. + +## Checklist: Feature or Bug-Fix Completion + +Before considering a task done: + +- ✓ A failing test existed before implementation (or skip-TDD was explicitly requested). +- ✓ Implementation was minimal and aimed at passing the test only. +- ✓ Refactoring happened only after tests were green. +- ✓ Focused tests passed for all changed packages. +- ✓ Broader validation was run when risk or scope justified it. +- ✓ Coverage gates were evaluated per changed module/class (target 80%, low bound 65%, fail below 50%). +- ✓ Behavioral parity expectations were preserved unless change was explicitly requested. +- ✓ Security scanning passed: `gosec ./...` and `govulncheck ./...` without unacknowledged findings. +- ✓ Dependency injection properly applied: dependencies injected via constructors, not globals; interfaces define contracts; concrete implementations satisfy interfaces. +- ✓ Commits were created at checkpoints (red test, green implementation, changelog). +- ✓ Changelog was updated with a separate docs-only commit.