Skip to content

Latest commit

 

History

History
555 lines (418 loc) · 18 KB

File metadata and controls

555 lines (418 loc) · 18 KB

Agent Development Standards

This document outlines the coding standards and best practices for AI agents and developers working on the basic-setup project.

Table of Contents


Skills

Skills follow the Opencode/Project Agent skill format and live under .agents/skills/<name>/SKILL.md. See .agents/README.md for the instructions index.


Work Snapshot Usage

Use .agents/work-snapshot.local.md as a local handoff aid, not as source-of-truth state.

  1. Read at session start

    • If the snapshot exists, read it before taking action to recover intent, recent decisions, and proposed next steps.
  2. Check staleness before relying on it

    • Treat updated_at as advisory only.
    • Compare snapshot recency against current date/time and recent git activity (git log, current branch, PR/check status) before trusting operational details.
  3. Prefer authoritative state for execution

    • Use git/GitHub state (working tree, commits, PR checks, mergeability) as the authoritative source for current truth.
    • Use snapshot content mainly for context, rationale, and handoff continuity.
  4. Refresh after milestones

    • Update the snapshot after major transitions (merge/rebase, conflict resolution, docs/version bumps, CI/check changes, review-state changes) and before ending a session.

Scope Control

When implementing work tied to an issue/PR, proactively detect scope creep and preserve reviewable units.

Git action guardrail

  • Never create commits or push branch updates unless the user explicitly asks for a commit/push in the current session.
  • Staging and local validation are allowed as preparation, but commit/push is opt-in only.
  • If a user states they always handle commit/push themselves, treat that as standing preference and never commit/push unless explicitly asked in that session.
  • Default behavior: do not commit or push unless asked; when work appears ready to commit, proactively propose a commit message draft for the user.
  1. Detect scope creep early

    • Treat newly identified, non-blocking improvements as potential follow-up scope, not automatic additions.
    • Examples: adjacent hardening, separate automation, or unrelated workflow polish.
    • Use an aggressive default: if a discovered change is not required for current acceptance criteria or to fix a blocking defect, classify it as out-of-scope.
  2. Pause and classify discovered work

    • If work is required to complete the current acceptance criteria or fix a blocking defect, keep it in scope.
    • If work is useful but not required, mark it out-of-scope for the current issue/PR.
  3. Ask before expanding scope

    • Present out-of-scope work to the user and ask whether to expand current scope or defer.
    • Default recommendation: keep the current PR focused and defer non-blocking work.
    • If the user explicitly approves scope expansion, treat that approval as authoritative and proceed with the accepted expansion.
  4. If deferring, open a follow-up issue

    • Create a new issue with clear summary, rationale, and acceptance criteria.
    • Link it to the relevant parent tracker and cross-reference the current issue/PR.
  5. Keep current PR focused and document the choice

    • Continue implementing only the scoped work in the current branch/PR unless user approves expansion.
    • Add a short PR comment and/or PR body note stating what was deferred and where it will be tracked.
    • Update planning docs when they maintain ordered execution lists.

Merge Conflict Resolution

When rebasing/merging long-lived branches, preserve mainline freshness and avoid silent regressions.

  1. Never resolve by downgrading

    • Prefer newer dependency/tool versions from main unless a documented compatibility, security, compliance, or operational-support issue requires pinning lower.
    • Treat version decreases (actions, language/toolchain, module/dependency, CLI semver) as regressions by default.
  2. Keep and extend changelog history

    • Never delete existing main changelog entries during conflict resolution.
    • Keep all existing released entries intact and add branch-relevant changes to the current branch/version entry per project bump policy.
  3. Protect release/version files

    • Resolve resources/version.yaml and bsctl/static/resources/constants.yaml to at least the latest upstream version; when conflicts occur, bump forward from upstream as needed instead of reverting to an older value.
    • Do not reduce version values during conflict resolution.
  4. Verify semantic parity after conflict resolution

    • Compare rebased branch vs origin/main and verify there are no unintended reversions in pinned versions, toolchain levels, or workflow actions.
    • Run targeted checks affected by conflicts (for example docs/version checks when CHANGELOG.md or version files conflict).
  5. Document any intentional downgrade

    • If a lower version is intentionally kept for compatibility, record the reason in the PR and changelog entry so reviewers can validate the tradeoff.

Response Option Framing

When presenting implementation options, make tradeoffs explicit and easy to choose.

  1. When a minimal path is offered, expose deeper options

    • If you present a "minimal" approach, also offer to provide "recommended" and "full" variants.
    • Keep minimal scoped to acceptance criteria and immediate unblock.
  2. Use a consistent option ladder

    • minimal: least change to satisfy requirements safely.
    • recommended: balanced durability/maintainability with moderate scope.
    • full: comprehensive hardening/completeness with larger scope.
  3. State scope and impact per option

    • Include what changes, expected risk/noise, and validation depth.
    • Call out when recommended/full would be scope expansion and require explicit user approval.
  4. Respect explicit user direction

    • If the user picks minimal/recommended/full (or explicitly approves scope expansion), follow that direction as authoritative.

Go Standards

Idiomatic Go Practices

Follow these industry-standard Go practices:

  1. Code Organization

    • Use meaningful package names that reflect their purpose
    • Keep packages focused and cohesive
    • Avoid circular dependencies
  2. Naming Conventions

    • Use camelCase for private identifiers
    • Use PascalCase for exported identifiers
    • Use descriptive names that clearly indicate purpose
    • Avoid stuttering (e.g., http.HTTPServerhttp.Server)
  3. Error Handling

    • Always check and handle errors explicitly
    • Don't panic in library code (use errors instead)
    • Wrap errors with context using fmt.Errorf with %w verb
    • Return errors as the last return value
  4. Formatting and Style

    • Use gofmt or goimports to format code
    • Follow the Effective Go guidelines
    • Use golint and go vet to catch common issues
  5. Concurrency

    • Use channels for communication between goroutines
    • Protect shared state with mutexes or use channels
    • Always document when a function starts a goroutine
    • Use context for cancellation and timeouts
  6. Testing

    • Write table-driven tests
    • Use meaningful test names (e.g., TestFunctionName_Scenario)
    • Test both success and error paths
    • Use subtests for related test cases

Example Go Code

// Good: Idiomatic Go
func ProcessUser(ctx context.Context, userID string) (*User, error) {
    if userID == "" {
        return nil, fmt.Errorf("userID cannot be empty")
    }

    user, err := fetchUserFromDB(ctx, userID)
    if err != nil {
        return nil, fmt.Errorf("failed to fetch user %s: %w", userID, err)
    }

    return user, nil
}

References


Bash Standards

Shell Script Best Practices

Follow these industry-standard Bash practices:

  1. Shebang

    • Use #! /usr/bin/env bash for portability
    • Note the space after #! as per project convention
  2. Error Handling

    • Use set -e to exit on error
    • Use set -u to treat unset variables as errors (optional but recommended)
    • Use set -o pipefail to catch errors in pipes
    • Trap errors for cleanup: trap 'cleanup' EXIT ERR
  3. Variable Quoting

    • Always quote variables: "$variable" not $variable
    • Use "${variable}" for clarity in complex expressions
    • Quote command substitutions: "$(command)"
  4. Validation and Checks

    • Validate inputs at the start of the script
    • Check file existence before operations
    • Validate expected formats (e.g., semver, dates)
  5. Functions

    • Use functions for reusable code blocks
    • Document function purpose with comments
    • Declare local variables with local
  6. Logging and Debugging

    • Use echo for user-facing messages
    • Direct errors to stderr: echo "Error: message" >&2
    • Exit with non-zero status on errors

Example Bash Script

#! /usr/bin/env bash

# Script description: Brief description of what this script does

set -e  # Exit on error
set -o pipefail  # Catch errors in pipes

# Constants
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly CONFIG_FILE="${SCRIPT_DIR}/config.yaml"

# Validate prerequisites
if [ ! -f "$CONFIG_FILE" ]; then
    echo "Error: Config file not found at $CONFIG_FILE" >&2
    exit 1
fi

# Function with proper structure
process_data() {
    local input_file="$1"
    local output_file="$2"
    
    if [ -z "$input_file" ] || [ -z "$output_file" ]; then
        echo "Error: Missing required arguments" >&2
        return 1
    fi
    
    # Process the data
    cat "$input_file" > "$output_file"
}

# Main logic
main() {
    local input="${1:-}"
    
    if [ -z "$input" ]; then
        echo "Usage: $0 <input_file>" >&2
        exit 1
    fi
    
    process_data "$input" "output.txt"
    echo "Processing complete"
}

# Run main with all arguments
main "$@"

References


Documentation Standards

Code Documentation

  1. Comments

    • Write clear, concise comments explaining "why", not "what"
    • Keep comments up-to-date with code changes
    • Use complete sentences with proper punctuation
  2. Function/Method Documentation

    • Document all exported functions/methods
    • Include parameters, return values, and error conditions
    • Provide usage examples for complex functions
  3. README Files

    • Include project overview and purpose
    • Document installation and setup steps
    • Provide usage examples
    • List prerequisites and dependencies
  4. CHANGELOG

    • Follow Keep a Changelog format
    • Use semantic versioning
    • Categorize changes: Added, Changed, Deprecated, Removed, Fixed, Security

Example Documentation

// ProcessPayment processes a payment transaction for the given amount.
// It validates the payment details, charges the payment method, and
// returns a transaction ID on success.
//
// Parameters:
//   - ctx: Context for cancellation and timeouts
//   - paymentID: Unique identifier for the payment
//   - amount: Payment amount in cents (must be positive)
//
// Returns:
//   - transactionID: Unique transaction identifier
//   - error: nil on success, or an error describing the failure
//
// Example:
//   txID, err := ProcessPayment(ctx, "pay_123", 1000)
//   if err != nil {
//       log.Fatalf("Payment failed: %v", err)
//   }
func ProcessPayment(ctx context.Context, paymentID string, amount int) (string, error) {
    // Implementation
}

Testing Standards

Testing Best Practices

  1. Test Coverage

    • Aim for at least 80% code coverage
    • Test both happy path and error cases
    • Include edge cases and boundary conditions
  2. Test Organization

    • Use table-driven tests for multiple scenarios
    • Group related tests using subtests
    • Keep tests independent and isolated
  3. Test Naming

    • Use descriptive test names: TestFunction_Scenario_ExpectedBehavior
    • Example: TestProcessPayment_InvalidAmount_ReturnsError
  4. Mocking and Fixtures

    • Use interfaces for dependencies to enable mocking
    • Keep test fixtures minimal and focused
    • Clean up resources after tests
  5. Integration Tests

    • Separate unit tests from integration tests
    • Use build tags for integration tests: // +build integration
    • Document setup requirements for integration tests

Example Test

func TestProcessUser_ValidInput_ReturnsUser(t *testing.T) {
    tests := []struct {
        name    string
        userID  string
        want    *User
        wantErr bool
    }{
        {
            name:    "valid user ID",
            userID:  "user123",
            want:    &User{ID: "user123", Name: "John"},
            wantErr: false,
        },
        {
            name:    "empty user ID",
            userID:  "",
            want:    nil,
            wantErr: true,
        },
    }

    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            got, err := ProcessUser(context.Background(), tt.userID)
            if (err != nil) != tt.wantErr {
                t.Errorf("ProcessUser() error = %v, wantErr %v", err, tt.wantErr)
                return
            }
            if !reflect.DeepEqual(got, tt.want) {
                t.Errorf("ProcessUser() = %v, want %v", got, tt.want)
            }
        })
    }
}

Version Bumping and CHANGELOG

Semantic Versioning

Follow Semantic Versioning 2.0.0:

  • MAJOR version: Incompatible API changes
  • MINOR version: New functionality, backward compatible
  • PATCH version: Backward compatible bug fixes

Version Bumping Process

  1. Determine Version Type

    • Breaking changes → MAJOR bump
    • New features → MINOR bump
    • Bug fixes → PATCH bump
    • Dependency updates (Dependabot) → PATCH bump
  2. Update Files

    • Update version in resources/version.yaml (and mirror bsctl/static/resources/constants.yaml during transition)
    • Add CHANGELOG entry with date and changes
    • Follow the CHANGELOG format
  3. CHANGELOG Format

    ## [X.Y.Z] - YYYY-MM-DD
    ### Added
    - New feature description
    
    ### Changed
    - Change description
    
    ### Fixed
    - Bug fix description
  4. Automated Bumping

    • Dependabot PRs are automatically bumped via workflow
    • Manual PRs require manual version bump
    • Every MR/PR should include at least one version bump and matching CHANGELOG entry unless explicitly exempted
    • Default to one bump per PR branch; follow-up commits should update the existing current-version entry rather than creating another version
    • Only bump again on the same PR when explicitly requested by the user or required by release policy
    • If a docs bump is already present on the branch, rerun docs-bump checks after each meaningful change to reduce missed changelog/version drift
    • Always ensure the branch is bumped before merging

Example CHANGELOG Entry

## [0.1.5] - 2026-01-17
### Changed
- Bump actions/checkout from v4 to v6
- Updated dependency parsing logic for better robustness

### Fixed
- Fixed CHANGELOG formatting for proper entry separation

Linting and Code Quality

Go Linting

  1. Tools

    • gofmt / goimports: Formatting
    • golint: Style checks
    • go vet: Static analysis
    • golangci-lint: Comprehensive linting (recommended)
    • staticcheck: Advanced static analysis
  2. Running Linters

    # Format code
    gofmt -w .
    
    # Run go vet
    go vet ./...
    
    # Run golangci-lint
    golangci-lint run
  3. CI Integration

    • All linters must pass in CI
    • Configure linters in .golangci.yml
    • Fix all warnings before merging

Bash Linting

  1. Tools

    • shellcheck: Shell script static analysis
    • shfmt: Shell script formatter
  2. Running Linters

    # Check scripts
    shellcheck script.sh
    
    # Format scripts
    shfmt -w script.sh
  3. Common Issues to Avoid

    • Unquoted variables
    • Using == in [ ] (use = or [[ instead)
    • Not checking command exit codes
    • Missing error handling

General Code Quality

  1. Pre-commit Checks

    • Format code before committing
    • Run relevant linters
    • Ensure tests pass locally
  2. Code Review

    • Address all review comments
    • Update based on feedback
    • Ensure CI passes before requesting review
  3. Continuous Improvement

    • Refactor complex code
    • Update documentation
    • Add tests for bug fixes

Additional Resources


Note: This document is a living standard and will be updated as the project evolves. All contributors should follow these guidelines to maintain code quality and consistency.