Skip to content

Latest commit

 

History

History
121 lines (79 loc) · 7.59 KB

File metadata and controls

121 lines (79 loc) · 7.59 KB

Contributing to Sortie

Sortie turns issue tracker tickets into autonomous coding agent sessions - a single Go binary that orchestrates workspaces, retries, and state reconciliation for AI coding agents. The full picture is in the README.

The project follows a spec-first model: docs/architecture.md defines every entity, state machine, and validation rule. The implementation conforms to it. This matters because if you are fixing a bug in the orchestrator, the architecture doc tells you what the correct behavior is. You do not need to reverse-engineer intent from the code.

Finding something to work on

Browse open issues and look for the labels good first issue and help wanted. These are curated for newcomers and do not require deep familiarity with the codebase.

If nothing catches your eye, test coverage and documentation fixes are always useful and require no prior discussion:

# Find packages with low coverage
make test-coverage | grep -v '100.0%' | sort -k3 -n

For larger work - new features, new adapters, architectural changes - open an issue first to discuss the approach. To talk it through before writing anything down, join the Discord server. Decisions still land in the issue, so they stay findable.

Setup

Requirements: Go 1.26.1 (see go.mod), golangci-lint, gopls, and lychee for documentation changes.

git clone https://github.com/sortie-ai/sortie.git
cd sortie
make test    # runs all tests with -race
make build   # compiles to ./sortie
make lint    # golangci-lint and comment style of changed files
make fmt     # gofmt + goimports
make lint-docs  # links and anchors in tracked Markdown files
make test-darwin  # darwin-only tests; macOS only, fails on any other host

All commands go through the Makefile. If make test passes, the change is safe to submit.

The SQLite dependency is modernc.org/sqlite -a pure-Go driver. No C compiler needed.

Integration tests

Integration tests are gated by environment variables and skip cleanly without them:

SORTIE_JIRA_TEST=1 SORTIE_JIRA_ENDPOINT="..." SORTIE_JIRA_API_KEY="..." \
  make test PKG=./internal/tracker/jira/...

SORTIE_GITHUB_TEST=1 SORTIE_GITHUB_TOKEN="ghp_..." SORTIE_GITHUB_PROJECT="owner/repo" \
  make test PKG=./internal/scm/github/...

SORTIE_CLAUDE_TEST=1 ANTHROPIC_API_KEY="sk-..." \
  make test PKG=./internal/agent/claude/...

GitHub integration tests also accept an optional SORTIE_GITHUB_ISSUE_ID variable (a valid issue number in the configured repo) to enable FetchIssueByID and FetchIssueStatesByIDs test cases.

You do not need access to Jira, GitHub, or Claude to contribute. The unit test suite covers the vast majority of the codebase.

Making a change

Small changes (docs, typos, test coverage, bug fixes in a single package): fork, fix, test, submit a PR. No ceremony needed.

Medium changes (multi-file bug fixes, new test scenarios, adapter improvements): read the relevant section of docs/architecture.md before implementing. The spec defines what correct behavior looks like, so reading it first prevents wasted effort.

Large changes (new features, new adapters, orchestrator changes): open an issue to discuss the design before writing code. The architecture doc is the source of truth -changes that contradict it will not be merged. If you believe the spec itself should change, that is a valid conversation to have in an issue.

Code conventions

The linter config in .golangci.yml catches most issues. Beyond what the linter enforces:

  • Naming: core packages use generic names (agent_*, tracker_*, session_*). Adapter-specific names (jira_*, claude_*) belong only inside their adapter package.
  • Errors: wrap with context (fmt.Errorf("operation: %w", err)), no capitals, no trailing punctuation. Use the error categories in internal/domain/errors.go.
  • Logging: log/slog with typed slog.Attr constructors. Derive loggers via logging.WithIssue and logging.WithSession.
  • Templates: Option("missingkey=error") on every text/template - strict mode is mandatory.
  • Shared adapter helpers: don't re-implement a helper already extracted to a shared package (internal/httpkit, internal/typeutil, internal/issuekit, or an adapter-family package like internal/scm/scmcore). A contract test in internal/adaptertest fails make test if a tracker, SCM, agent, or notifier package re-declares one of these under a local name.

Testing conventions

  • Table-driven tests with t.Parallel() at both test and subtest level.
  • t.Helper() as the first line of every test helper.
  • t.TempDir() for filesystem isolation, t.Setenv() for environment variables.
  • Error assertions via errors.As() / errors.Is(), never string matching.
  • Failure format: FuncName(input) = got, want expected.
  • Standard library only - no third-party assertion frameworks.
  • Fixtures live in testdata/ within each package.
  • A test that only a macOS host can run lives in a _darwin_test.go file and never skips: the macOS CI job selects tests by that suffix and fails on a skip. An env-gated integration test skips by design, so none lives in such a file.

Commits and PRs

Commit messages follow Conventional Commits:

feat(orchestrator): add stall detection for running sessions
fix(workspace): reject symlinks escaping workspace root
test(tracker): cover pagination edge cases in Jira adapter

PRs use the template. One logical change per PR. CI lints, tests (including on Windows, and the darwin-only tests on macOS), and builds the binary - all must pass.

What will not be merged

  • CGo or any dependency requiring a C compiler.
  • Adapter-specific logic in core packages (internal/orchestrator/, internal/domain/).
  • Weakened workspace path containment or input sanitization - these are security boundaries.
  • Behavior that contradicts docs/architecture.md.
  • Changes the author cannot explain or justify under review (see below).

If you are unsure whether a change fits, open an issue. A five-minute conversation saves hours of work.

AI-assisted contributions

Sortie is primarily developed with AI coding agents. Contributions using AI tools are welcome under the same quality bar: the code must be correct, spec-conformant, tested, and reviewed by you before submitting. make test and make lint must pass - not just "the agent said it works".

Whoever opens the pull request owns it. You are accountable for every line you submit and for its conformance to the conventions, spec, and tests described here, regardless of how the change was produced: by hand, by a teammate, by a contractor, or by an AI agent. We have no preference among those. What we do not accept is "the agent decided" as the explanation for why a change is the way it is. If you cannot explain a change under review, it is not ready to merge. The verification gate is identical for everyone: make lint and make test pass because you ran them and read the output, not because a tool reported success.

Security

Workspace path containment and input sanitization are security boundaries, not convenience features. Changes to internal/workspace/ receive additional scrutiny. If you find a vulnerability, report it privately rather than opening a public issue.

License

Contributions are licensed under Apache License 2.0.