Developing
August 27, 2026 · View on GitHub
You're modifying Issue Orchestrator — adding features, fixing bugs, or extending the system.
Dev setup
git clone https://github.com/BruceBGordon/issue-orchestrator.git
cd issue-orchestrator
make venv
source .venv/bin/activate
pytest tests/unit/ -x -q # Verify the unit suite passes
If you're working on a feature branch, use a git worktree to keep the base repo clean:
make worktree-create BRANCH=my-feature
This one-shot command creates ../issue-orchestrator-wt-my-feature from
HEAD and runs the complete worktree setup there. It does not depend on a
shell cd carrying over between commands. To choose a different starting ref
or destination, use:
make worktree-create \
BRANCH=my-feature \
BASE_REF=origin/main \
WORKTREE_PATH=/absolute/path/to/my-feature-worktree
If Git creates the worktree but setup fails, the worktree is kept and the
command prints an explicit make -C <path> worktree-setup retry command.
Understand the architecture
Architecture diagram — The hex diagram shows how everything connects: entry points, control plane, ports, adapters, external systems.
AGENTS.md — This is the primary conventions guide for contributors. It's written to be directly actionable for coding agents, but the architecture rules and workflow constraints apply equally to humans. The key sections:
| Section | What you'll learn |
|---|---|
| Architecture Principles | Hexagonal, DI, layered separation, labels as truth, agent intent vs orchestrator authority |
| Key Ports | The foundational Protocol interfaces and a pointer to the full port set in ports/ |
| Events vs Logs | Structured events drive the UI; logs are for humans. Never parse log text in code. |
| Fail-Fast Design | No fallbacks, no silent degradation. Crash on unexpected state. |
| Conventions | Where ports live, where adapters live, how to test, how to emit events |
Find what you need to change
The codebase follows strict layered separation:
| Layer | Directory | Responsibility |
|---|---|---|
| Control | control/ | Decisions, policy, state advancement. Pure logic, no I/O. |
| Observation | observation/ | Gather facts. No decisions, no mutations. |
| Adapters | adapters/ | Concrete external-system integrations. |
| Execution | execution/ | Runtime services, provider factories, and orchestration support code. |
| Ports | ports/ | Protocol interfaces. Contracts between layers. |
| Domain | domain/ | Models, state machines, events. |
If you're adding a new external integration, you'll add a Protocol in ports/ and a concrete adapter in adapters/, then wire it through the composition root and execution/provider layer as needed. If you're changing decision logic, that's control/. The runtime facade lives in infra/orchestrator.py and should keep delegating rather than owning policy.
Testing
pytest tests/unit/ -v # Full unit suite
pytest tests/unit/test_foo.py -v # Single file
pytest tests/e2e/ -v # E2E tests (requires gh auth)
Mock at port boundaries, not internal functions. Create a mock that implements the Protocol, inject it, test the control logic in isolation. See Testing Guide for patterns and fixtures.
Import-linter enforces architecture. Control cannot import execution. Ports cannot import outer layers. If your change violates a boundary, make validate will catch it.
Key workflows to understand
| Topic | Doc | When you need it |
|---|---|---|
| How agents complete work | entrypoints/cli_tools/coding_done.py / entrypoints/cli_tools/reviewer_done.py + AGENTS "Agent Intent, Orchestrator Authority" | Modifying completion processing |
| Code review loop | Review Workflow | Modifying review, rework, or tech lead |
| Hook enforcement | Hooks Architecture | Modifying safety guardrails |
| State machines | domain/state_machines/ | Changing issue or review lifecycle |
| Events and observability | AGENTS "Events vs Logs" | Adding new observable behavior |
Submitting changes
- Commit first, then run
make validate-pr. The gate records its green result against the currentHEADSHA, and the git pre-push hook reuses that record on push. Validate on an uncommitted tree and the dirty guard rejects it outright; commit after a green run and the record points at the parent commit, so the whole suite re-runs at push time - If the gate fails, fix it, commit the fix, and re-run
make validate-pr— the green must land on the commit that actually gets pushed. Nevergit stashwork that belongs in this push; stashing leavesHEADon the commit you are about to replace and the stashed change never gets pushed. Classify each dirty file before staging anything: commit what belongs in the push, delete or.gitignoreonly artifacts you created and can positively identify as disposable, and preserve everything else. Never delete or revert a file you did not create, and never commit one just to clear the guard make validate-pris a superset ofmake validate(validate-pr-rawruns the same_validate-implsuite plus the agent lane), so run one or the other — running both validates the standard suite twice- CI mirrors
make validate-prby splitting the fast validate job and the agent-backed simulated/integration slices across separate required jobs - Use
make validate-pr-rawonly when you intentionally need to force the full uncached local suite at the same HEAD; it records nothing, so the pre-push hook will re-run the same suite - Tests must pass. If tests fail, fix them — don't defer.
- CONTRIBUTING.md covers running tests from forks
Development docs reference
These docs in docs/development/ cover specific topics in depth:
| Doc | When to read |
|---|---|
| Testing Guide | Test patterns, fixtures, mocking |
| Troubleshooting | Debugging sessions, hooks, common issues |
| Review Workflow | Code review pipeline, exchange mechanisms |
| Debugging | Event system debugging |
| Caching & ETags | GitHub API caching implementation |
| GitHub Auth Setup (Dev) | Token resolution chain internals and GitHub App auth |