Initial project structure documentation and task breakdown for Multi-Agent Dev Orchestrator (Temporal + Go).
6.9 KiB
Poimen Workflows — Development Guidelines
This project orchestrates multi-agent software development tasks using Temporal + Go. The repo is organized as:
statemachine/— Temporal workflow definitions (deterministic state machines)action/— Temporal activity definitions (units of work / LLM calls)cmd/— CLI entry points (worker registration, workflow starter)prompts/— LLM prompt templates (Go-embedded, live-updatable)internal/— Shared config/locking utilitiestests/— Unit tests viago.temporal.io/sdk/testsuite
Code Standards
Go Style
Follow golang-skills conventions from ~/.claude/skills/golang-skills/:
- Error handling mandatory; no naked
_ =discards. - Idiomatic naming:
ctxfor context,errfor error returns. - Interfaces kept narrow; one struct per concern.
- No over-generalization for hypothetical use.
- Documentation via comments only where the WHY is non-obvious.
Testing
- Every
statemachine/*change ships with atestsuite-based test intests/. - Unit tests use
go.temporal.io/sdk/testsuite.WorkflowTestEnvironmentwith mocked activities. - Activities are tested in isolation before integrating into workflows.
- No literal timeout/retry values in
action/*code — all come fromOrchestratorConfig.Tuningat runtime.
Activity Design
Activities stay side-effect-isolated (one concern per file) so they remain independently reusable across projects.
action/git.go— all git operationsaction/skills.go— skill prep / pi commandaction/planner.go— Planner LLM callaction/judge.go— Judge LLM callaction/implementer.go— Implementer LLM + tool-call loopaction/lessons.go— Lessons store read/write
No composite activities like PrepareAndImplement; that's a workflow's job, not an activity's.
Config as Data
No hardcoded numbers. Every timeout, retry count, backoff coefficient, stream timeout — all come from OrchestratorConfig.Tuning (or role-specific PromptSpec.Model), read at execution time. This lets:
update-tuningsignal to dynamically adjust timeouts without redeployment- Planner activity to recommend per-task overrides
- Pi 504 handler to learn and persist
StreamTimeoutacrossContinueAsNewcycles
No LLM model hardcoded. Every model ID comes from ModelSpec.ModelID, passed as data.
Concurrency & Locking
Target repo sits on shared filesystem. Git concurrency is safe by construction:
- Task units use isolated worktrees (
git worktree add -b task/T0.x ...) - Commits within a worktree don't need a lock; git serializes object writes
- Only
CloneRepoActivity,GitPushActivity,GitSquashMergeActivityneed theorchestrator.lock, since they mutate the main working tree / refs
Monitor for deadlocks: if two workflows try to push simultaneously, the lock will serialize them. Test with concurrent tasks enabled.
Task Board Format
Each task row in the board specifies:
- ID:
T0.1,T0.2, etc. - Description: One-line scope
- Status:
[ ](todo),[x](done) - Branch:
task/T0.x(created when task starts, merged to main on submilestone complete) - Verification: Specific criterion that marks it done (e.g. "Unit test passes", "E2E run completes")
A task is NOT marked done until its verification step passes. This mirrors the Judge role's own job: no hallucinated completion.
Submilestones & Merges
When all tasks in T0 (T0.1 through T0.9) pass their verification:
- Orchestrator calls
GitSquashMergeActivityto merge alltask/T0.*branches intomainas a single squashed commit - All
task/T0.*branches and worktrees are cleaned up - Main branch is the canonical history; the 9 subtask commits are compacted into one
This demonstrates the very mechanism the system orchestrates: a working software dev pipeline with multiple agents collaborating on a shared codebase, guarded by state checks (Judge), and landing changes via deterministic git workflow.
Historical Lessons
Lessons live at tasks/.orchestrator/lessons/<TaskID>.jsonl — per-task file of failed attempts. When a Judge calls a task failure, the UpdateLessonsActivity appends {Attempt, Critique, FailedApproachSummary, Timestamp}. On retry, ReadLessonsActivity injects the last N entries into the Implementer's prompt as "known errors — do not repeat this time".
Lessons flush to git only as part of the Planner's board commit, not on every retry — keeps history clean.
Skills & Preparation
Required skill sources are listed in OrchestratorConfig.Skills as a list of references (e.g. ["~/.claude/skills/golang-skills", "custom-skill-repo"]). PrepareSkillsActivity runs once per config change, cloning/fetching them onto the shared FS via the pi command. All retry/backoff/timeout is delegated to Temporal's retry machinery, with special handling for 504 (stream timeout learning).
Environment & Secrets
All external system access is via environment variables, loaded at worker startup:
TEMPORAL_NAMESPACE,TEMPORAL_TLS_CERT,TEMPORAL_TLS_KEY— Temporal cluster connectionANTHROPIC_API_KEY— LLM API key- Any target-repo-specific credentials (e.g. git SSH key) are assumed already available on the shared FS (e.g. via Kubernetes secret mount)
Use homelab's vsource .env pattern to load from a .env file during local development.
Observability
- Temporal Web UI (
temporal.riotpiao.com:8080or similar) shows workflow execution, signal delivery, activity retries - Workflow
current-configquery returns liveOrchestratorConfig(useful for debugging which tuning values are in effect) - Activity heartbeats (
activity.RecordHeartbeat) are sent after each tool-call iteration, visible in Temporal's activity details - Lessons file grows as retries happen; inspect it on failure to understand what the Implementer is learning
Deployment & Homelab Integration
This repo is application-layer code against the homelab's Temporal cluster. The orchestrator runs as a Kubernetes pod with:
- Persistent volume (PVC) for the shared FS where target repos are cloned
- Network access to Temporal + Anthropic APIs
- Git SSH key mounted for cloning target repos
The Kubernetes manifests + Helm charts live in the homelab repo under k8s/ and follow homelab's GitOps workflow (commit → ArgoCD sync). This repo's CI/CD (GitHub Actions or Forgejo Actions) builds and pushes the Docker image; the homelab repo triggers a new pod deploy on image push.
Questions & Debugging
If a task is marked done but you suspect it's wrong:
- Re-run its verification step manually
- Check the unit test against the latest code
- For e2e tasks, review Temporal Web UI logs + board file + lessons file on the target repo
- Update the board and task description if the criterion was misunderstood
If code doesn't compile or tests fail:
- Check Go version and Temporal SDK version match
- Run
go mod tidyandgo mod vendorif dependencies drift - Look for hardcoded values or model IDs that should be config instead