# formulas/run-architect.toml — Architect formula # # Executed by architect-run.sh via cron — strategic decomposition of vision # issues into development sprints. # # This formula orchestrates the architect agent's workflow: # Step 1: Preflight — bash handles state management: # - Fetch open vision issues from Forgejo API # - Fetch open architect PRs on ops repo # - Fetch merged architect PRs (already pitched visions) # - Filter: remove visions with open PRs, merged sprints, or sub-issues # - Select up to 3 remaining vision issues for pitching # Step 2: Stateless pitch generation — for each selected issue: # - Invoke claude -p with: vision issue body + codebase context # - Model NEVER calls Forgejo API — only generates pitch markdown # - Bash creates the ops PR with pitch content # - Bash posts the ACCEPT/REJECT footer comment # Step 3: Sprint PR creation with questions (issue #101) (one PR per pitch) # Step 4: Answer parsing + sub-issue filing (issue #102) # # Architecture: # - Bash script (architect-run.sh) handles ALL state management # - Model calls are stateless — no Forgejo API access, no memory between calls # - Dedup is automatic via bash filters (no journal-based memory needed) # - Max 3 open architect PRs at any time # # AGENTS.md maintenance is handled by the gardener (#246). name = "run-architect" description = "Architect: strategic decomposition of vision into sprints" version = 2 model = "opus" [context] files = ["VISION.md", "AGENTS.md"] # Prerequisite tree loaded from ops repo (ops: prefix) # Sprints directory tracked in ops repo [[steps]] id = "preflight" title = "Preflight: bash-driven state management and issue selection" description = """ This step performs preflight checks and selects up to 3 vision issues for pitching. IMPORTANT: All state management is handled by bash (architect-run.sh), NOT the model. Architecture Decision: Bash-driven orchestration with stateless model calls - The model NEVER calls Forgejo API during pitching - Bash fetches all data from Forgejo API (vision issues, open PRs, merged PRs) - Bash filters and deduplicates (no model-level dedup or journal-based memory) - For each selected issue, bash invokes stateless claude -p (model only generates pitch) - Bash creates PRs and posts footer comments (no model API access) Bash Actions (in architect-run.sh): 1. Fetch open vision issues from Forgejo API: GET /repos/{owner}/{repo}/issues?labels=vision&state=open 2. Fetch open architect PRs from ops repo: GET /repos/{owner}/{repo}/pulls?state=open 3. Fetch merged sprint PRs: GET /repos/{owner}/{repo}/pulls?state=closed (filter merged=true) 4. Filter out visions that: - Already have open architect PRs (check PR body for issue number reference) - Have in-progress label - Have open sub-issues (check for 'Decomposed from #N' pattern) - Have merged sprint PRs (decomposition already done) 5. Select up to (3 - open_architect_pr_count) remaining vision issues 6. If no issues remain AND no responses to process, signal PHASE:done If open architect PRs exist, handle accept/reject responses FIRST (see Capability B below). After handling existing PRs, count remaining open architect PRs and calculate pitch_budget. ## Multi-pitch selection (up to 3 per run) After handling existing PRs, determine how many new pitches can be created: pitch_budget = 3 - For each available pitch slot: 1. From the vision issues list, skip any issue that already has an open architect PR 2. Skip any issue that already has the `in-progress` label 3. Check for existing sub-issues filed from this vision issue 4. Check for merged sprint PRs referencing this vision issue 5. From remaining candidates, pick the most unblocking issue first 6. Add to ARCHITECT_TARGET_ISSUES array Skip conditions: - If no vision issues are found, signal PHASE:done - If pitch_budget <= 0 (already 3 open architect PRs), skip pitching - If all vision issues already have open architect PRs, signal PHASE:done - If all vision issues have open sub-issues, skip pitching - If all vision issues have merged sprint PRs, skip pitching Output: - Sets ARCHITECT_TARGET_ISSUES as a JSON array of issue numbers to pitch (up to 3) """ [[steps]] id = "research_pitch" title = "Stateless pitch generation: model generates content, bash creates PRs" description = """ IMPORTANT: This step is executed by bash (architect-run.sh) via stateless claude -p calls. The model NEVER calls Forgejo API — it only reads context and generates pitch markdown. Architecture: - Bash orchestrates the loop over ARCHITECT_TARGET_ISSUES - For each issue: bash fetches issue body from Forgejo API, then invokes stateless claude -p - Model receives: vision issue body + codebase context (VISION.md, AGENTS.md, prerequisites.md) - Model outputs: sprint pitch markdown ONLY (no API calls, no side effects) - Bash creates the PR and posts the ACCEPT/REJECT footer comment For each issue in ARCHITECT_TARGET_ISSUES, bash performs: 1. Fetch vision issue details from Forgejo API: - GET /repos/{owner}/{repo}/issues/{issue_number} - Extract: title, body 2. Invoke stateless claude -p with prompt: "Write a sprint pitch for this vision issue. Output only the pitch markdown." Context provided: - Vision issue #N: - Vision issue body - Project context (VISION.md, AGENTS.md) - Codebase context (prerequisites.md, graph section) - Formula content 3. Model generates pitch markdown (NO API CALLS): # Sprint: <sprint-name> ## Vision issues - #N — <title> ## What this enables <what the project can do after this sprint that it can't do now> ## What exists today <current state — infrastructure, interfaces, code that can be reused> ## Complexity <number of files/subsystems, estimated sub-issues> <gluecode vs greenfield ratio> ## Risks <what could go wrong, what breaks if this is done badly> ## Cost — new infra to maintain <what ongoing maintenance burden does this sprint add> <new services, cron jobs, formulas, agent roles> ## Recommendation <architect's assessment: worth it / defer / alternative approach> IMPORTANT: Do NOT include design forks or questions yet. The pitch is a go/no-go decision for the human. Questions come only after acceptance. 4. Bash creates PR: - Create branch: architect/sprint-{pitch-number} - Write sprint spec to sprints/{sprint-slug}.md - Create PR with pitch content as body - Post footer comment: "Reply ACCEPT to proceed with design questions, or REJECT: <reason> to decline." - Add in-progress label to vision issue Output: - One PR per vision issue (up to 3 per run) - Each PR contains the pitch markdown - If ARCHITECT_TARGET_ISSUES is empty, skip this step """ [[steps]] id = "sprint_pr_creation" title = "Sprint PR creation with questions (issue #101) — handled by bash" description = """ IMPORTANT: PR creation is handled by bash (architect-run.sh) during the pitch step. This step is for documentation only — the actual PR creation happens in research_pitch. Architecture: - Bash creates PRs during stateless pitch generation (step 2) - Model has no role in PR creation — no Forgejo API access - This step describes the PR format for reference PR Format (created by bash): 1. Branch: architect/sprint-{pitch-number} 2. Sprint spec file: sprints/{sprint-slug}.md Contains the pitch markdown from the model. 3. PR via Forgejo API: - Title: architect: <sprint summary> - Body: plain markdown text from model output - Base: main (or PRIMARY_BRANCH) - Head: architect/sprint-{pitch-number} - Footer comment: "Reply ACCEPT to proceed with design questions, or REJECT: <reason> to decline." 4. Add in-progress label to vision issue: - Look up label ID: GET /repos/{owner}/{repo}/labels - Add label: POST /repos/{owner}/{repo}/issues/{issue_number}/labels After creating all PRs, signal PHASE:done. ## Forgejo API Reference All operations use the Forgejo API with Authorization: token ${FORGE_TOKEN} header. ### Create branch ``` POST /repos/{owner}/{repo}/branches Body: {"new_branch_name": "architect/<sprint-slug>", "old_branch_name": "main"} ``` ### Create/update file ``` PUT /repos/{owner}/{repo}/contents/<path> Body: {"message": "sprint: add <sprint-slug>.md", "content": "<base64-encoded-content>", "branch": "architect/<sprint-slug>"} ``` ### Create PR ``` POST /repos/{owner}/{repo}/pulls Body: {"title": "architect: <sprint summary>", "body": "<markdown-text>", "head": "architect/<sprint-slug>", "base": "main"} ``` **Important: PR body format** - The body field must contain plain markdown text (the raw content from the model) - Do NOT JSON-encode or escape the body — pass it as a JSON string value - Newlines and markdown formatting (headings, lists, etc.) must be preserved as-is ### Add label to issue ``` POST /repos/{owner}/{repo}/issues/{index}/labels Body: {"labels": [<label-id>]} ``` ## Forgejo API Reference All operations use the Forgejo API with `Authorization: token ${FORGE_TOKEN}` header. ### Create branch ``` POST /repos/{owner}/{repo}/branches Body: {"new_branch_name": "architect/<sprint-slug>", "old_branch_name": "main"} ``` ### Create/update file ``` PUT /repos/{owner}/{repo}/contents/<path> Body: {"message": "sprint: add <sprint-slug>.md", "content": "<base64-encoded-content>", "branch": "architect/<sprint-slug>"} ``` ### Create PR ``` POST /repos/{owner}/{repo}/pulls Body: {"title": "architect: <sprint summary>", "body": "<markdown-text>", "head": "architect/<sprint-slug>", "base": "main"} ``` **Important: PR body format** - The `body` field must contain **plain markdown text** (the raw content from the scratch file) - Do NOT JSON-encode or escape the body — pass it as a JSON string value - Newlines and markdown formatting (headings, lists, etc.) must be preserved as-is ### Close PR ``` PATCH /repos/{owner}/{repo}/pulls/{index} Body: {"state": "closed"} ``` ### Delete branch ``` DELETE /repos/{owner}/{repo}/git/branches/<branch-name> ``` ### Get labels (look up label IDs by name) ``` GET /repos/{owner}/{repo}/labels ``` ### Add label to issue (for in-progress on vision issue) ``` POST /repos/{owner}/{repo}/issues/{index}/labels Body: {"labels": [<label-id>]} ``` ### Remove label from issue (for in-progress removal on REJECT) ``` DELETE /repos/{owner}/{repo}/issues/{index}/labels/{label-id} ``` """