# 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 utilities - `tests/` — Unit tests via `go.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: `ctx` for context, `err` for 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 a `testsuite`-based test in `tests/`. - Unit tests use `go.temporal.io/sdk/testsuite.WorkflowTestEnvironment` with mocked activities. - Activities are tested in isolation before integrating into workflows. - No literal timeout/retry values in `action/*` code — all come from `OrchestratorConfig.Tuning` at runtime. ### Activity Design Activities stay side-effect-isolated (one concern per file) so they remain independently reusable across projects. - `action/git.go` — all git operations - `action/skills.go` — skill prep / pi command - `action/planner.go` — Planner LLM call - `action/judge.go` — Judge LLM call - `action/implementer.go` — Implementer LLM + tool-call loop - `action/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-tuning` signal to dynamically adjust timeouts without redeployment - Planner activity to recommend per-task overrides - Pi 504 handler to learn and persist `StreamTimeout` across `ContinueAsNew` cycles **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`, `GitSquashMergeActivity` need the `orchestrator.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: 1. Orchestrator calls `GitSquashMergeActivity` to merge all `task/T0.*` branches into `main` as a single squashed commit 2. All `task/T0.*` branches and worktrees are cleaned up 3. 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/.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 connection - `ANTHROPIC_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:8080` or similar) shows workflow execution, signal delivery, activity retries - Workflow `current-config` query returns live `OrchestratorConfig` (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: 1. Re-run its verification step manually 2. Check the unit test against the latest code 3. For e2e tasks, review Temporal Web UI logs + board file + lessons file on the target repo 4. Update the board and task description if the criterion was misunderstood If code doesn't compile or tests fail: 1. Check Go version and Temporal SDK version match 2. Run `go mod tidy` and `go mod vendor` if dependencies drift 3. Look for hardcoded values or model IDs that should be config instead