Shift the guardrail from prose prompt constraints into Forgejo's permission
layer. architect-bot loses all write access on the project repo (now read-only
for context gathering). Sub-issues are produced by a new filer-bot identity
that runs only after a human merges a sprint PR on the ops repo.
Changes:
- architect-run.sh: remove all project-repo writes (add_inprogress_label,
close_vision_issue, check_and_close_completed_visions); add ## Sub-issues
block to pitch format with filer:begin/end markers
- formulas/run-architect.toml: add Sub-issues schema to pitch format; strip
issue-creation API refs; document read-only constraint on project repo
- lib/formula-session.sh: remove Create issue curl template from
build_prompt_footer (architect cannot create issues)
- lib/sprint-filer.sh (new): parser + idempotent filer using FORGE_FILER_TOKEN;
parses filer:begin/end blocks, creates issues with decomposed-from markers,
adds in-progress label, handles vision lifecycle closure
- .woodpecker/ops-filer.yml (new): CI pipeline on ops repo main-branch push
that invokes sprint-filer.sh after sprint PR merge
- lib/env.sh, .env.example, docker-compose.yml: add FORGE_FILER_TOKEN for
filer-bot identity; add filer-bot to FORGE_BOT_USERNAMES
- AGENTS.md: add Filer agent entry; update in-progress label docs
- .woodpecker/agent-smoke.sh: register sprint-filer.sh for smoke test
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Use FORGE_TOKEN_OVERRIDE (set before sourcing env.sh) instead of
post-source FORGE_TOKEN reassignment in all five agent run scripts.
The override mechanism in lib/env.sh:98-100 survives re-sourcing from
nested shells and claude -p tool invocations.
Affected scripts: architect-run.sh, planner-run.sh, gardener-run.sh,
predictor-run.sh, supervisor-run.sh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce FORGE_API_BASE (bare API root without repo path) in lib/env.sh
and lib/load-project.sh. Replace all cross-repo curl calls in
architect-run.sh that incorrectly used ${FORGE_API}/repos/${FORGE_OPS_REPO}
(which expanded to .../repos/owner/repo/repos/owner/ops-repo) with
${FORGE_API_BASE}/repos/${FORGE_OPS_REPO}.
Also fix a same-repo label URL that duplicated the repos segment.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three fixes:
1. architect-run.sh:722 — extract `.result` not `.content` from claude JSON
output. All other callers (dev-agent, formula-session) use `.result`;
this was the direct cause of every pitch being empty.
2. lib/agent-sdk.sh — reset `_AGENT_LAST_OUTPUT=""` at the top of each
`agent_run` call so stale data from a prior invocation can't bleed
into the next caller when claude crashes or returns empty.
3. lib/agent-sdk.sh — scope the diagnostics file by `$LOG_AGENT` instead
of hardcoding `dev/`. Concurrent agents (architect, gardener, planner,
predictor) no longer clobber each other's diag output.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Removed state=closed filter so all issues with "Decomposed from #N" are found
- Per-issue state check in all_subissues_closed() correctly handles open/closed
agent_run() stores its output in $_AGENT_LAST_OUTPUT but never emits
it to stdout. The old subshell capture always yielded an empty string,
so pitches silently failed even after the signature fix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
agent_run() now adds -p, --output-format, --max-turns, --dangerously-skip-permissions,
and --model internally. The old call site passed these flags explicitly, causing the
prompt to be parsed as "-p" and claude to error with "Input must be provided".
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use $'\n' instead of literal \n in summary comment builder
- Query closed issues in Method 1 to find sub-issues regardless of state
- Document automated vision issue closure lifecycle in AGENTS.md
Implementation:
- Added detect_approved_pending_questions() function to identify approved PRs
that have no ## Design forks section and no Q1:, Q2: comments yet.
- Modified response processing block to handle three session modes:
1. questions_phase: Resume session for processing Q&A answers
2. start_questions: Fresh session to post initial design questions
3. pitch: Original behavior for new pitch generation
- Added build_architect_prompt_for_mode() function to generate appropriate
prompts for each session mode.
- When an approved PR is detected, the agent posts initial design questions
(Q1:, Q2:, etc.) and adds the ## Design forks section, transitioning the
PR into the existing questions phase.
This fixes the issue where approved architect PRs would sit indefinitely
because the agent had no path to start the design conversation.
- fetch_pr_review_decision now sets REVIEW_DECISION/REVIEW_GUIDANCE globals
instead of printf to stdout (multiline guidance broke cut-based parsing)
- ACCEPT handler instructs model to update PR body with Design forks section
so fetch_pr_answers can detect the answers phase on subsequent runs
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add bash precondition checks to skip model invocation when:
- No vision issues exist AND no open architect PRs to handle
- Already at max 3 open architect PRs AND no ACCEPT/REJECT responses to process
This avoids $0.28+ empty runs where the model reads context and concludes 'no work'.
The model is only invoked when there's actual work: new pitches or response processing.
When the architect processes human answers to design questions (answer_parsing step),
it now resumes the session from the research/questions run instead of starting fresh.
This preserves Claude's deep codebase understanding from the research phase, ensuring
sub-issues include specific file references and implementation details.
Changes:
- architect-run.sh: Added detect_questions_phase() to check if PR is in questions phase
(has `## Design forks` section and question comments). If so, resume the session
from SID_FILE to preserve context.
- formulas/run-architect.toml: Documented session resumption behavior in answer_parsing step.
Session is only preserved when PR is in questions-awaiting-answers phase. Fresh sessions
are started for new pitches (no stale context from old sprints).