How issue watchers use labels
For each issue with the trigger label the watcher sets status labels, so you can follow along in GitHub:
| Label (default name) | Meaning |
|---|---|
factory:working | Working — nothing to do, it continues by itself |
factory:needs-info | Waiting for you — questions: reply on the issue, or reply /defaults |
factory:waiting-approval | Waiting for you — approval: reply /approve or /reject on the issue |
factory:done | Done — nothing to do |
factory:failed | Failed; the comment on the issue starts with what you need to do, then says what happened, why, the kind of problem, what was already tried and your options, with the failing output. Remove the label to start over, or resume the run on its page to continue at the failed step. If the Foundry itself failed, the comment says so and names the fix |
The descriptions on GitHub say the same in short. They are set when the labels are created and
refreshed at every server start. The trigger label and the review label (vars.review_plan_label)
get one too. If the review label is the same as the trigger label, the one description says both.
Each watcher's card on the Watchers page lists the labelled issues it is not working on right now under What happens next, with the same lines as the Dashboard. A watcher error is a line too; the raw error is under Error details.
Every check also compares the label with the newest run and fixes what is safe to fix: a run that
is working (for example approved or resumed on its page) gets factory:working, and a label that
does not match the run (say factory:failed while the run waits for approval) is corrected. A
factory:done label is left alone, and an issue without a status label starts over. If you close
an issue on GitHub while its run is still working or waits for approval, nothing is changed; the
watcher says so (see Why is nothing happening?) and you decide
whether to cancel the run. Every label change is in the card's Recent activity. Next to the
repository the card shows last successful check …, also when the newest check failed.
The needs-info and waiting labels stay only while the Foundry really waits for you. Once you have
answered the questions and the issue waits for something else (another story, the release pull
request, the budget, the limit of new stories per check or a story that is running), the label is
removed at the next check, so the issue looks like every other story that waits. When the release
pull request holds it, the thing to do (merge it) is on that pull request. A run that asked, was
answered and cannot continue yet shows factory:working. The label comes back only for new,
unanswered questions. A waiting label on an issue that has no run is left alone: remove the label
to start the issue.
Parked stories. To park a story, remove its Foundry labels (or only the trigger label). A run that stopped with questions, or was cancelled while it was stopped, is then parked at the next check: the watcher marks the run as parked, and the overview shows "parked — not tracked by a watcher" with nothing to do. Nothing continues the run, and answering on the issue does not start it. Needs you leaves the story out too. Add the trigger label again and the story starts over with a new run, which the overview then follows; the old stopped run does not block it. A story that still has the trigger label after its stopped run was cancelled is not started again by itself; the overview says "cancelled — nothing to do". To start it again, remove and add the trigger label, or resume the run on its page. Stopped runs that wait for a release or a code area are not parked.
More options are set in ~/.spaghetti-code-foundry/config.yaml (the form keeps them when you edit the
watcher). This is the watcher for the one-label pipeline:
watchers:
- id: webshop
source: issues
flow: issue-gitflow # feature branch per issue → merged into develop
precheck_flow: epic-questions # ask all open questions for new issues first
github_repo: acme/webshop
label: Factory_go
exclude_labels: [wontfix]
status_labels: {working: Factory_working, done: Factory_done, needs_info: Factory_needs_info, waiting: Factory_waiting, failed: Factory_ERROR}
remove_on_done: [Factory_go]
max_per_tick: 2 # issues on different code areas are coded in parallel (bug stories are not counted)
priority_labels: [bug] # stories with these labels go first (the default)
dependency_done_labels: [Factory_done]
vars:
test_cmd: ./gradlew test
docs_required: docs/CHANGELOG.md
union_merge_files: docs/CHANGELOG.md # merges keep both sides' entries
agent_env: JAVA_HOME=/path/to/jdk # extra environment for the coding agents
develop_branch: develop
risk_threshold: "75" # plans scoring above this wait for /approve
- id: webshop-release
source: schedule
flow: release-daily # daily tests + build on develop, PR develop → main
github_repo: acme/webshop
at: "17:00"
timezone: Europe/Berlin
task: Daily release pull request develop → main
| Option | What it does |
|---|---|
exclude_labels | Never touch issues with any of these labels |
status_labels | Your own names for the status labels above |
remove_on_done | Labels to remove when a run succeeds (e.g. the trigger label) |
one_at_a_time | Never run two of this watcher's runs at once |
wait_for_dependencies | On by default: an issue with a Depends on / Blocked by section waits until those issues are closed (or have a dependency_done_labels label) |
dependency_done_labels | Labels that also count as "done" for dependencies, e.g. Factory_done |
precheck_flow | Run this flow once over all new labelled issues before any is started (epic-questions asks every owner decision up front) |
auto_defaults | true: the watcher answers the Foundry's questions itself with the recommendations (it posts /defaults on the issue), so nothing waits for a person. At most twice per issue; if the planner still asks after that, a person answers. Default false |
pause_while_pr_open | Start nothing while a PR from a branch with this prefix is open (for the older two-label pipeline) |
comment_on_failure | On by default: post the failure reason and output on the issue |
status_comment | On by default: keep one status comment on every issue the watcher follows (see "The status comment") |
owner | Only for watchers in config.yaml: the e-mail of the account that owns the watcher's runs and may read and approve them. Empty: the first admin. It must be an account. A watcher of a repository uses the repository's owner |
The issue list. Each issue watcher check reads the repository's issue list once: open and closed issues, newest first, 100 per GitHub call, at most 2,000 issues (20 calls). Watchers of the same repository share that read. The dependency check uses this list. The Foundry keeps the title, state, labels, links and dependencies of each issue, but no issue text, in the data folder (issue-lists/). If the list has more than 2,000 issues, the stored list is marked as cut and older issues are not seen by the dependency check. If the read fails, the check uses the previous way of reading open issues and logs the reason.
Coding agents run the build themselves. In the issue flows the coding agent may run the
project's build and test commands (./gradlew, mvn, npm, pytest, go test, cargo,
make) and read-only git commands — so it can check its own work and, for example, regenerate
test fixtures. Pushing is never allowed. If the build needs environment settings (such as
JAVA_HOME), put them in the agent_env variable: KEY=value pairs separated by ; or new
lines. PATH, tokens and the Foundry's own variables can't be set this way. In a run that never
uses the machine's login (see "What runs sign in with"), names that start with GH_, GITHUB_,
GIT_, SSH_ or XDG_, and LC_ALL, are ignored too.
Bug stories go first#
A story with a bug label is repaired before the Foundry builds anything new. The watcher option priority_labels lists the labels (default [bug], not case sensitive). Set priority_labels: [] to turn it off for a watcher.
- What goes first: in every check the watcher handles bug stories before all others: questions up front, starting, resuming and answering. When a slot is free, a waiting bug story gets it before any other story, also before stories that were queued earlier and stories of other repositories. Among bug stories the oldest issue goes first.
- No per-check limit: bug stories are not counted against
max_per_tick. Concurrency, the daily budget andone_at_a_timestill limit what runs. - Code areas: when several stories wait for the same code area, a bug story gets it first when it becomes free. The other stories wait up to one check longer.
- What a bug story still waits for: its own "Depends on" stories, the answers to its own questions, a daily budget or usage limit, and an open release pull request (
pause_while_pr_open). It goes first after that. - Nothing running is stopped. A bug story only takes the next free slot.
- Taking the label off ends it: the story goes back to its place in the queue.
- Where you see it: a bug story has a "goes first" mark on the board and in the queue. Another story that waits for it says "waits: a bug story goes first".
- Runs started by hand from the Runs page get no priority. Resume, approve and reject on the run page of a bug story do.
The status comment#
Every issue an issue watcher follows gets one status comment that says what happens next. It looks like this:
**Nothing needed from you** — it is being worked on.
Nothing to do, it continues by itself.
- **Where:** this issue
_This comment is kept up to date by the Spaghetti Code Foundry. It is edited, never posted again. Other comments are history._
- Edited, never a second one. The Foundry changes the text of the same comment when the state changes. An edit sends no notification. Other comments (questions, plans, approvals, failures) are history and stay as they are.
- When it is your move, the first line starts with What you need to do. Otherwise it says Nothing needed from you and why.
- Done. Once the work is finished it says it is done. If the work waits for a scheduled release (a schedule watcher with
at), it says it waits for that release, and says it is done after the release ran. - Closed while the run works. It asks you to cancel the run if the work is no longer wanted. When the run ends it says it is done.
- No longer followed. If the issue is closed or loses the watcher's label, the last text says the Foundry no longer follows it (or that it is done, if its run succeeded). A closed-issue check that fails or is cut short changes nothing.
- Two watchers on one issue (for example a plan and a code watcher) share one comment. It shows the record of the one that is working, else the one that waits for you.
- Plain words. The comment uses the wording for a reader of the issue: no raw error text, folder, setting or amount of money. Pages of the Foundry app are named, not linked; the details are in the app.
- Switch it off with
status_comment: falseon the watcher. A switched-off, removed or disabled watcher leaves its comments as they are. - Extra copies. If the Foundry finds more than one status comment of its own account on an issue, it keeps the oldest and removes the others (at most 5 per check; the rest follows at the next check).
- A failure is only in the log. If a comment cannot be written, the check goes on; the log has a line
status comment #<n>: …and the next check tries again. - After an upgrade, the first check adds one new comment (one notification) to every open issue the watcher follows, at most 30 per check. The Foundry remembers which issues have one in
status-comments.jsonin its data folder (issue numbers only), so it can write a last text when an issue leaves the list, also after a restart.