# formulas/run-architect.toml — Architect formula
#
# Executed by architect-run.sh via polling loop — 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 - <number of open architect PRs remaining after handling>

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: <title>
   - 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, scheduled tasks, 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.

## Approved PR → Initial design questions (issue #570)

When a sprint pitch PR receives an APPROVED review but has no `## Design forks`
section and no Q1:, Q2: comments yet, the architect enters a new state:

1. detect_approved_pending_questions() identifies this state
2. A fresh agent session starts with a special prompt
3. The agent reads the approved pitch, posts initial design questions (Q1:, Q2:, etc.)
4. The agent adds a `## Design forks` section to the PR body
5. The PR transitions into the questions phase, where the existing Q&A loop takes over

This ensures approved PRs don't sit indefinitely without design conversation.

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}
```
"""
