forgeo-cli 0.10.0__tar.gz → 0.11.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/CHANGELOG.md +24 -1
- forgeo_cli-0.11.0/PKG-INFO +188 -0
- forgeo_cli-0.11.0/README.md +134 -0
- forgeo_cli-0.11.0/docs/agent-contract.md +78 -0
- forgeo_cli-0.11.0/docs/backlog.md +217 -0
- forgeo_cli-0.11.0/docs/cli-reference.md +173 -0
- forgeo_cli-0.11.0/docs/configuration.md +263 -0
- forgeo_cli-0.11.0/docs/getting-started.md +116 -0
- forgeo_cli-0.11.0/docs/index.md +37 -0
- forgeo_cli-0.11.0/docs/web-console-api.md +220 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/install.sh +1 -1
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/mkdocs.yml +1 -1
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/pyproject.toml +1 -1
- forgeo_cli-0.11.0/scripts/test-github-backlog-e2e.sh +225 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/__init__.py +1 -1
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/agent.py +18 -26
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/backlog.py +196 -40
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/backlog_github.py +126 -177
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/backlog_gitlab.py +123 -169
- forgeo_cli-0.11.0/src/forgeo/backlog_issue_base.py +366 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/backlog_jira.py +113 -103
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/central.py +185 -88
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/config.py +15 -12
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/daemon.py +10 -5
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/forgeo.py +158 -59
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/git.py +76 -9
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/io.py +2 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/models.py +149 -175
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/paths.py +1 -1
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/runs.py +5 -4
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/setup.py +170 -138
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/validate.py +36 -52
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/web/central/central.css +12 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/web/central/central.js +86 -3
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/web/central/instance.html +21 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/web/style.css +16 -1
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/web_common.py +5 -7
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_cli.py +2 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_web.py +2 -1
- forgeo_cli-0.10.0/PKG-INFO +0 -254
- forgeo_cli-0.10.0/README.md +0 -200
- forgeo_cli-0.10.0/docs/agent-contract.md +0 -154
- forgeo_cli-0.10.0/docs/backlog.md +0 -515
- forgeo_cli-0.10.0/docs/cli-reference.md +0 -374
- forgeo_cli-0.10.0/docs/configuration.md +0 -536
- forgeo_cli-0.10.0/docs/getting-started.md +0 -221
- forgeo_cli-0.10.0/docs/index.md +0 -67
- forgeo_cli-0.10.0/docs/web-console-api.md +0 -695
- forgeo_cli-0.10.0/src/forgeo/backlog_issue_base.py +0 -183
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/.github/workflows/ci.yml +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/.gitignore +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/CONTRIBUTING.md +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/LICENSE +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/config/nginx-forgeo.conf +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/docs/img/console.png +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/docs/img/demo.gif +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/docs/img/logo.png +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/docs/img/og.png +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/docs/img/title.svg +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/forgeo.spec +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/scripts/__init__.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/scripts/render_homebrew_formula.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/__main__.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/backlog_http.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/cli.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/daemon_control.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/instances.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/notify.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/oauth.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/update.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/web/central/index.html +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/src/forgeo/web/central/login.html +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/conftest.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_agent.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_backlog.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_backlog_github.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_backlog_gitlab.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_backlog_http.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_backlog_jira.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_daemon.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_factory.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_git.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_install.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_instances.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_io.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_models.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_oauth.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_paths.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_remote_backlog_cycle.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_render_homebrew.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_runs.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_setup.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_update.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_web_common.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/tests/test_web_lock.py +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/www/404.html +0 -0
- {forgeo_cli-0.10.0 → forgeo_cli-0.11.0}/www/index.html +0 -0
|
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.11.0] - 2026-08-31
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **REVIEW status with feature-branch workflow** (`review_mode: branch`). When enabled, a successful agent run no longer commits directly to `main` as `COMPLETED`: the engine creates a feature branch `forgeo/review/<TASK_ID>` (via `review_branch_prefix`, default `forgeo/review/`), commits there, pushes, and marks the task `REVIEW`. Tasks in `REVIEW` block dependants (like `BLOCKED`/`FAILED`) while independent tasks keep running.
|
|
15
|
+
- Per-task `review_required` override (`bool | null`): `true` forces branch+REVIEW, `false` forces direct `COMPLETED`, `null` inherits `review_mode`. Editable via `PATCH /api/instances/<name>/tasks/<id>` and persisted across all providers (file/http body, hidden `<!-- forgeo: {...} -->` block on GitHub/GitLab, Jira issue property `review_branch`/`review_commit_sha`).
|
|
16
|
+
- Engine-managed `review_branch` and `review_commit_sha` on `Task` (set on `REVIEW`, cleared on leave), carried through document stores (`DocumentBacklogStore.set_review`/`complete_review`/`request_changes`) and issue stores via `forgeo-review` label (`forgeo_labels()`) plus engine state.
|
|
17
|
+
- Web console: dedicated **REVIEW** kanban column, review-branch display on cards/modals, and human-in-the-loop actions `Complete` (`POST …/complete-review` → `COMPLETED` after manual merge) and `Request changes` (`POST …/request-changes` → `OPEN` for rework). Issue providers also show `external_url`/board links for the branch.
|
|
18
|
+
- Generic webhook now supports `review` event (`WEBHOOK_EVENTS += "review"`, `notify_webhook_events` validation), sent by `Forgeo._run_task` on the `REVIEW` transition via `GitManager.create_review_branch`/`a_create_review_branch` and `_commit_on_review_branch` (creates/resets branch, commits, pushes, switches back to `branch` and hard-resets `main`).
|
|
19
|
+
- `HttpBacklog`/`JSONBacklog`/`IssueBacklogBase` now expose `set_review`/`complete_review`/`request_changes` and central dashboard routes `POST /api/instances/<name>/tasks/<id>/{complete-review,request-changes}`. Shared helpers `_issue_provider_base`, `_github_repo_root`, `_gitlab_issues_root`, `forgeo_labels`, `extract_issue_number/labels`, `bump_state_counter` unify Jira/GitHub/GitLab handling.
|
|
20
|
+
- `config/forgeo.yaml` and `docs/configuration.md` document `review_mode` and `review_branch_prefix`; `mkdocs.yml` `site_description` updated.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Docs restructured and condensed (~55% shorter) while preserving full coverage: `README`, `docs/agent-contract.md`, `docs/backlog.md`, `docs/cli-reference.md`, `docs/configuration.md`, `docs/getting-started.md`, `docs/index.md`, `docs/web-console-api.md` rewritten for scannability (tables, endpoint summary, concise curl examples).
|
|
25
|
+
- `docs/backlog.md`, `docs/configuration.md`, `docs/web-console-api.md`, and `README` document the new REVIEW workflow, `REVIEW` task lifecycle, and new `review`-related API fields.
|
|
26
|
+
- Internal maintainability refactor: `backlog.py` helpers `_require_string`/`_require_string_list`/`_status_by_id`/`_set_agent_response`/`_blocked_notice`/`_blocker_sections`/`_render_block`, `forgeo.py` split of blocker/commit logic, `models.py` `_IssueFieldMappingBase`/`_IssueBacklogConfigBase`/`_PatAuthBase`/`_IssueWorkflowBase`/`_RepoBacklogConfigBase` extraction, `IssueBacklogBase` shared `_call`/`_labels`/`_get_issue`/`_search_all`/`_task_from_issue`/`list_tasks`/`get_task`/`validate_connection`/`bump_failed_wait`/`_update_issue_labels`, `GitManager.ensure_branch`/`current_branch`/`create_review_branch`/`commit_all_on_branch` plus async wrappers, `central.py` `_github_web_base` exact-host rewrite and `_external_board_url`/`_external_issue_url` helpers, `config.py` `_maybe_resolve`, `validate.py`/`daemon.py`/`runs.py`/`paths.py`/`io.py`/`web_common.py`/`setup.py` lint-driven cleanups.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `ruff`/`mypy` compliance across the review branch (BLE001/S110/F541 suppressions in `setup.py`, type narrowing in `Forgeo`, `src/forgeo/backlog.py:21`, etc.); `pytest` green including new `REVIEW` column/counts and delete tests.
|
|
31
|
+
|
|
10
32
|
## [0.10.0] - 2026-08-24
|
|
11
33
|
|
|
12
34
|
### Added
|
|
@@ -407,7 +429,8 @@ Initial release of the scheduled, agent-driven software forgeo.
|
|
|
407
429
|
overlapping-run skipping.
|
|
408
430
|
- Dogfooding docs removed; local configs kept out of the repository.
|
|
409
431
|
|
|
410
|
-
[Unreleased]: https://github.com/lucaGazzola/forgeo/compare/v0.
|
|
432
|
+
[Unreleased]: https://github.com/lucaGazzola/forgeo/compare/v0.11.0...HEAD
|
|
433
|
+
[0.11.0]: https://github.com/lucaGazzola/forgeo/compare/v0.10.0...v0.11.0
|
|
411
434
|
[0.10.0]: https://github.com/lucaGazzola/forgeo/compare/v0.9.0...v0.10.0
|
|
412
435
|
[0.9.0]: https://github.com/lucaGazzola/forgeo/compare/v0.8.0...v0.9.0
|
|
413
436
|
[0.8.0]: https://github.com/lucaGazzola/forgeo/compare/v0.7.3...v0.8.0
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: forgeo-cli
|
|
3
|
+
Version: 0.11.0
|
|
4
|
+
Summary: A scheduled software forgeo: executes backlog tasks on main, refactors when idle, and writes BLOCKER.md when it needs human input.
|
|
5
|
+
Project-URL: Homepage, https://forgeo.org
|
|
6
|
+
Project-URL: Documentation, https://forgeo.org
|
|
7
|
+
Project-URL: Repository, https://github.com/lucaGazzola/forgeo
|
|
8
|
+
Project-URL: Issues, https://github.com/lucaGazzola/forgeo/issues
|
|
9
|
+
Author: Forgeo Contributors
|
|
10
|
+
License: MIT License
|
|
11
|
+
|
|
12
|
+
Copyright (c) 2026 Software Forgeo Contributors
|
|
13
|
+
|
|
14
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
15
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
16
|
+
in the Software without restriction, including without limitation the rights
|
|
17
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
18
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
19
|
+
furnished to do so, subject to the following conditions:
|
|
20
|
+
|
|
21
|
+
The above copyright notice and this permission notice shall be included in all
|
|
22
|
+
copies or substantial portions of the Software.
|
|
23
|
+
|
|
24
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
25
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
26
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
27
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
28
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
29
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
30
|
+
SOFTWARE.
|
|
31
|
+
License-File: LICENSE
|
|
32
|
+
Keywords: agents,ai,automation,backlog,orchestration,scheduler
|
|
33
|
+
Classifier: Development Status :: 3 - Alpha
|
|
34
|
+
Classifier: Intended Audience :: Developers
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
41
|
+
Requires-Python: >=3.11
|
|
42
|
+
Requires-Dist: pydantic>=2.5
|
|
43
|
+
Requires-Dist: pyyaml>=6.0
|
|
44
|
+
Requires-Dist: rich>=13.7
|
|
45
|
+
Provides-Extra: dev
|
|
46
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
47
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
48
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
49
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
50
|
+
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
|
|
51
|
+
Provides-Extra: docs
|
|
52
|
+
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
<div align="center">
|
|
56
|
+
<img src="docs/img/logo.png" alt="Forgeo logo" width="128">
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div align="center">
|
|
60
|
+
<img src="docs/img/title.svg" alt="Forgeo" width="128">
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
[](https://github.com/lucaGazzola/forgeo/actions/workflows/ci.yml)
|
|
64
|
+
[](https://opensource.org/licenses/MIT)
|
|
65
|
+
|
|
66
|
+
<div align="center">
|
|
67
|
+
<img src="docs/img/demo.gif" alt="Forgeo running a backlog task end to end" width="720">
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
**Forgeo is a software factory for your coding agent.**
|
|
71
|
+
|
|
72
|
+
Give it a backlog and an agent CLI — Forgeo picks the next runnable task, runs the agent, and commits the result. It tracks progress in plain files and a web dashboard, and only interrupts you when a human decision is needed.
|
|
73
|
+
|
|
74
|
+
- **One task at a time** — oldest `OPEN` task whose dependencies are `COMPLETED` (or a `run_at` schedule). `REVIEW` blocks dependants but not independent tasks.
|
|
75
|
+
- **Agent-agnostic** — any CLI that reads `FORGEO_TASK` (aider, Claude, custom script).
|
|
76
|
+
- **Refactors when idle** — runs a refactoring pass when the backlog is empty.
|
|
77
|
+
- **Handles failure gracefully** — `BLOCKED` for human input (`BLOCKER.md`), `FAILED` with retry policy, snapshots for file backlogs, Telegram/webhook notifications.
|
|
78
|
+
- **Optional review** — `review_mode: branch` commits to `forgeo/review/TASK-001`, marks `REVIEW`, pushes and waits for human merge → `Complete`.
|
|
79
|
+
|
|
80
|
+
Requires a terminal, a git repo, and an agent CLI.
|
|
81
|
+
|
|
82
|
+
## Quickstart
|
|
83
|
+
|
|
84
|
+
Full walkthrough: [Getting Started](docs/getting-started.md).
|
|
85
|
+
|
|
86
|
+
### 1. Install
|
|
87
|
+
|
|
88
|
+
Pick one (no root; re-run to upgrade):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
brew install lucaGazzola/forgeo/forgeo # Homebrew, no Python needed
|
|
92
|
+
curl -fsSL https://forgeo.org/install.sh | bash # binary or pip fallback
|
|
93
|
+
pipx install forgeo-cli # Python 3.11+
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 2. Init
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
forgeo init
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Wizard in your project root. Creates `forgeo.yaml` and `.forgeo/` (backlog, logs, blockers). Prompts for backlog provider (`file` / `github` / `gitlab` / `jira` / `http`) and agent command. For `github` it auto-detects `owner/repo` and can persist `GITHUB_TOKEN` to `~/.config/forgeo/`.
|
|
103
|
+
|
|
104
|
+
### 3. Fill the backlog
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# file provider: edit .forgeo/backlog.json (see Backlog format)
|
|
108
|
+
# github/gitlab/jira/http: configured in forgeo.yaml, then:
|
|
109
|
+
forgeo validate
|
|
110
|
+
|
|
111
|
+
# or add tasks from the dashboard:
|
|
112
|
+
forgeo web # http://0.0.0.0:8790 (use -d to keep it running)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+

|
|
116
|
+
|
|
117
|
+
### 4. Start
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
forgeo validate # dry run: config, repo, backlog, agent, locks
|
|
121
|
+
forgeo start # daemon in background, one cycle per interval_minutes
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Each cycle: pick task → run agent → commit/push (or `REVIEW` branch when `review_mode: branch`). Empty backlog → refactoring pass.
|
|
125
|
+
|
|
126
|
+
### Day-to-day
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
forgeo status # config, counts, next task, daemon state, last outcome
|
|
130
|
+
forgeo once # one cycle in foreground, no daemon
|
|
131
|
+
forgeo run --task TASK-012 # run a specific OPEN task now
|
|
132
|
+
forgeo stop / restart # stop or restart daemon
|
|
133
|
+
forgeo web # dashboard for all instances
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
`forgeo web` is open by default on `0.0.0.0:8790`. On a shared host use `forgeo web --token` for bearer auth — see [Web console](docs/web-console-api.md).
|
|
137
|
+
|
|
138
|
+
### Docker sandbox
|
|
139
|
+
|
|
140
|
+
Run the agent isolated:
|
|
141
|
+
|
|
142
|
+
```yaml
|
|
143
|
+
agent_sandbox: docker
|
|
144
|
+
agent_sandbox_image: your-image # must contain agent CLI + sh
|
|
145
|
+
agent_sandbox_network: none # default, no network
|
|
146
|
+
agent_sandbox_mounts: [~/.claude] # read-only mounts
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Repo is bind-mounted at the same path; task arrives as `FORGEO_TASK`. See [Configuration](docs/configuration.md).
|
|
150
|
+
|
|
151
|
+
### Multiple repos
|
|
152
|
+
|
|
153
|
+
One config per repo, fully independent (own backlog, logs, locks). Use the instance registry:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
forgeo instance add site-a --config /path/to/site-a/forgeo.yaml
|
|
157
|
+
forgeo start --name site-a
|
|
158
|
+
forgeo list # all instances
|
|
159
|
+
forgeo web # aggregate dashboard
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Documentation
|
|
163
|
+
|
|
164
|
+
| Topic | Doc |
|
|
165
|
+
| --- | --- |
|
|
166
|
+
| Install, init, first cycle | [Getting Started](docs/getting-started.md) |
|
|
167
|
+
| All `forgeo.yaml` keys | [Configuration](docs/configuration.md) |
|
|
168
|
+
| Task schema & statuses | [Backlog format](docs/backlog.md) |
|
|
169
|
+
| Agent env, exit codes, timeouts | [Agent contract](docs/agent-contract.md) |
|
|
170
|
+
| All CLI commands | [CLI reference](docs/cli-reference.md) |
|
|
171
|
+
| Dashboard & HTTP API | [Web console & HTTP API](docs/web-console-api.md) |
|
|
172
|
+
|
|
173
|
+
**Backlog providers:** [file](docs/backlog.md#a-json-file-backlog) · [HTTP](docs/backlog.md#a-backlog-over-http) · [Jira](docs/backlog.md#a-jira-backlog) · [GitHub](docs/backlog.md#a-github-backlog) · [GitLab](docs/backlog.md#a-gitlab-backlog) — file/HTTP exchange the full document; Jira/GitHub/GitLab sync issues individually. File backlogs are snapshotted (`backlog.json.bak`) before each run and restored if corrupt.
|
|
174
|
+
|
|
175
|
+
**Dashboard:** `forgeo web` aggregates every instance. For `file`/`http` it is the primary editor; for `jira`/`github`/`gitlab` it is a read-mostly mirror (links to native issues, surfaces `BLOCKED`/`FAILED` reasons and retry state).
|
|
176
|
+
|
|
177
|
+
## Develop
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
pip install -e ".[dev]"
|
|
181
|
+
pytest
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) (quality gates: `pytest`, `ruff check`, `mypy src/forgeo`).
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="docs/img/logo.png" alt="Forgeo logo" width="128">
|
|
3
|
+
</div>
|
|
4
|
+
|
|
5
|
+
<div align="center">
|
|
6
|
+
<img src="docs/img/title.svg" alt="Forgeo" width="128">
|
|
7
|
+
</div>
|
|
8
|
+
|
|
9
|
+
[](https://github.com/lucaGazzola/forgeo/actions/workflows/ci.yml)
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
|
|
12
|
+
<div align="center">
|
|
13
|
+
<img src="docs/img/demo.gif" alt="Forgeo running a backlog task end to end" width="720">
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
**Forgeo is a software factory for your coding agent.**
|
|
17
|
+
|
|
18
|
+
Give it a backlog and an agent CLI — Forgeo picks the next runnable task, runs the agent, and commits the result. It tracks progress in plain files and a web dashboard, and only interrupts you when a human decision is needed.
|
|
19
|
+
|
|
20
|
+
- **One task at a time** — oldest `OPEN` task whose dependencies are `COMPLETED` (or a `run_at` schedule). `REVIEW` blocks dependants but not independent tasks.
|
|
21
|
+
- **Agent-agnostic** — any CLI that reads `FORGEO_TASK` (aider, Claude, custom script).
|
|
22
|
+
- **Refactors when idle** — runs a refactoring pass when the backlog is empty.
|
|
23
|
+
- **Handles failure gracefully** — `BLOCKED` for human input (`BLOCKER.md`), `FAILED` with retry policy, snapshots for file backlogs, Telegram/webhook notifications.
|
|
24
|
+
- **Optional review** — `review_mode: branch` commits to `forgeo/review/TASK-001`, marks `REVIEW`, pushes and waits for human merge → `Complete`.
|
|
25
|
+
|
|
26
|
+
Requires a terminal, a git repo, and an agent CLI.
|
|
27
|
+
|
|
28
|
+
## Quickstart
|
|
29
|
+
|
|
30
|
+
Full walkthrough: [Getting Started](docs/getting-started.md).
|
|
31
|
+
|
|
32
|
+
### 1. Install
|
|
33
|
+
|
|
34
|
+
Pick one (no root; re-run to upgrade):
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
brew install lucaGazzola/forgeo/forgeo # Homebrew, no Python needed
|
|
38
|
+
curl -fsSL https://forgeo.org/install.sh | bash # binary or pip fallback
|
|
39
|
+
pipx install forgeo-cli # Python 3.11+
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Init
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
forgeo init
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Wizard in your project root. Creates `forgeo.yaml` and `.forgeo/` (backlog, logs, blockers). Prompts for backlog provider (`file` / `github` / `gitlab` / `jira` / `http`) and agent command. For `github` it auto-detects `owner/repo` and can persist `GITHUB_TOKEN` to `~/.config/forgeo/`.
|
|
49
|
+
|
|
50
|
+
### 3. Fill the backlog
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# file provider: edit .forgeo/backlog.json (see Backlog format)
|
|
54
|
+
# github/gitlab/jira/http: configured in forgeo.yaml, then:
|
|
55
|
+
forgeo validate
|
|
56
|
+
|
|
57
|
+
# or add tasks from the dashboard:
|
|
58
|
+
forgeo web # http://0.0.0.0:8790 (use -d to keep it running)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+

|
|
62
|
+
|
|
63
|
+
### 4. Start
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
forgeo validate # dry run: config, repo, backlog, agent, locks
|
|
67
|
+
forgeo start # daemon in background, one cycle per interval_minutes
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Each cycle: pick task → run agent → commit/push (or `REVIEW` branch when `review_mode: branch`). Empty backlog → refactoring pass.
|
|
71
|
+
|
|
72
|
+
### Day-to-day
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
forgeo status # config, counts, next task, daemon state, last outcome
|
|
76
|
+
forgeo once # one cycle in foreground, no daemon
|
|
77
|
+
forgeo run --task TASK-012 # run a specific OPEN task now
|
|
78
|
+
forgeo stop / restart # stop or restart daemon
|
|
79
|
+
forgeo web # dashboard for all instances
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`forgeo web` is open by default on `0.0.0.0:8790`. On a shared host use `forgeo web --token` for bearer auth — see [Web console](docs/web-console-api.md).
|
|
83
|
+
|
|
84
|
+
### Docker sandbox
|
|
85
|
+
|
|
86
|
+
Run the agent isolated:
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
agent_sandbox: docker
|
|
90
|
+
agent_sandbox_image: your-image # must contain agent CLI + sh
|
|
91
|
+
agent_sandbox_network: none # default, no network
|
|
92
|
+
agent_sandbox_mounts: [~/.claude] # read-only mounts
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Repo is bind-mounted at the same path; task arrives as `FORGEO_TASK`. See [Configuration](docs/configuration.md).
|
|
96
|
+
|
|
97
|
+
### Multiple repos
|
|
98
|
+
|
|
99
|
+
One config per repo, fully independent (own backlog, logs, locks). Use the instance registry:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
forgeo instance add site-a --config /path/to/site-a/forgeo.yaml
|
|
103
|
+
forgeo start --name site-a
|
|
104
|
+
forgeo list # all instances
|
|
105
|
+
forgeo web # aggregate dashboard
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Documentation
|
|
109
|
+
|
|
110
|
+
| Topic | Doc |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| Install, init, first cycle | [Getting Started](docs/getting-started.md) |
|
|
113
|
+
| All `forgeo.yaml` keys | [Configuration](docs/configuration.md) |
|
|
114
|
+
| Task schema & statuses | [Backlog format](docs/backlog.md) |
|
|
115
|
+
| Agent env, exit codes, timeouts | [Agent contract](docs/agent-contract.md) |
|
|
116
|
+
| All CLI commands | [CLI reference](docs/cli-reference.md) |
|
|
117
|
+
| Dashboard & HTTP API | [Web console & HTTP API](docs/web-console-api.md) |
|
|
118
|
+
|
|
119
|
+
**Backlog providers:** [file](docs/backlog.md#a-json-file-backlog) · [HTTP](docs/backlog.md#a-backlog-over-http) · [Jira](docs/backlog.md#a-jira-backlog) · [GitHub](docs/backlog.md#a-github-backlog) · [GitLab](docs/backlog.md#a-gitlab-backlog) — file/HTTP exchange the full document; Jira/GitHub/GitLab sync issues individually. File backlogs are snapshotted (`backlog.json.bak`) before each run and restored if corrupt.
|
|
120
|
+
|
|
121
|
+
**Dashboard:** `forgeo web` aggregates every instance. For `file`/`http` it is the primary editor; for `jira`/`github`/`gitlab` it is a read-mostly mirror (links to native issues, surfaces `BLOCKED`/`FAILED` reasons and retry state).
|
|
122
|
+
|
|
123
|
+
## Develop
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
pip install -e ".[dev]"
|
|
127
|
+
pytest
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) (quality gates: `pytest`, `ruff check`, `mypy src/forgeo`).
|
|
131
|
+
|
|
132
|
+
## License
|
|
133
|
+
|
|
134
|
+
MIT — see [LICENSE](LICENSE).
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Agent contract
|
|
2
|
+
|
|
3
|
+
The coding agent is **any shell command** that can:
|
|
4
|
+
|
|
5
|
+
1. Read the task from `FORGEO_TASK`.
|
|
6
|
+
2. Work on the repository in the current directory.
|
|
7
|
+
3. Report its outcome via **exit code**.
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
agent_command: "claude -p \"$FORGEO_TASK\""
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Environment
|
|
14
|
+
|
|
15
|
+
Launched with the repo as cwd and:
|
|
16
|
+
|
|
17
|
+
| Variable | Value |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `FORGEO_TASK` | Full instruction — project context (if `task_context`), title, description, and "Acceptance criteria:" when present. |
|
|
20
|
+
| `FORGEO_REPO` | Absolute repo path. |
|
|
21
|
+
| `FORGEO_BRANCH` | Target branch (default `main`). |
|
|
22
|
+
| `agent_env` keys | Extra vars from config. |
|
|
23
|
+
| Inherited env | Daemon's own environment. |
|
|
24
|
+
|
|
25
|
+
`FORGEO_*` always wins. For refactoring runs (empty backlog), `FORGEO_TASK` carries the `refactor_prompt` (task ID `REFACTOR`).
|
|
26
|
+
|
|
27
|
+
When `task_context` is set, its file contents are prepended under `# Project context` before `# Task`. Re-read each run; missing file is a warning.
|
|
28
|
+
|
|
29
|
+
## Exit codes
|
|
30
|
+
|
|
31
|
+
| Code | Outcome | What happens |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `0` | **SUCCESS** | `git add -A && git commit` as `<title> (#<id>)`, push if `remote` set, task → `COMPLETED`. |
|
|
34
|
+
| `no_changes_exit_code` (default `3`) | **SUCCESS, no changes** | Task → `COMPLETED` without commit. Tree must be clean. |
|
|
35
|
+
| `blocked_exit_code` (default `2`) | **BLOCKED** | Partial work committed as `<title> [partial]`, `blocker_reason` saved, notifications sent, task → `BLOCKED`, `BLOCKER.md` rendered next cycle. |
|
|
36
|
+
| anything else | **ERROR** | Changes discarded (`git reset --hard` + `clean -fd`), task → `FAILED`, `failure_reason` saved. |
|
|
37
|
+
|
|
38
|
+
`FAILED` stays `FAILED` until human reopens (or auto-retried via `failed_retry_max`). `BLOCKED` is never auto-retried.
|
|
39
|
+
|
|
40
|
+
## The no-change contract
|
|
41
|
+
|
|
42
|
+
Forgeo cannot distinguish "deliberately no changes" from "did nothing":
|
|
43
|
+
|
|
44
|
+
- Exit `0` with **unchanged tree** → retried `no_changes_retry_max` times in the same cycle, then `BLOCKED` (never `FAILED` or silent `COMPLETED`).
|
|
45
|
+
- To complete without code change, exit `no_changes_exit_code` with a **clean tree**. Reporting no-change while leaving uncommitted work → `FAILED`.
|
|
46
|
+
|
|
47
|
+
Refactoring passes are exempt — a refactor finding nothing to improve is a normal success.
|
|
48
|
+
|
|
49
|
+
## Timeouts
|
|
50
|
+
|
|
51
|
+
`agent_timeout_seconds` kills the agent after N seconds (`error: timed out after <n>s`). Unset = no timeout. A run overrunning `interval_minutes` is never killed — the next cycle is skipped. Tip: leave unset for long agents and rely on skip-on-overlap.
|
|
52
|
+
|
|
53
|
+
## Output
|
|
54
|
+
|
|
55
|
+
Stdout/stderr are prefixed `[stdout]`/`[stderr]` and the last **1000 lines** are kept. On `BLOCKED`, the agent's questions (or output) become `blocker_reason` and the `BLOCKER.md` section (last 10 lines). Per-run tails go to `runs.jsonl` (`run_output_lines`); per-task `agent_response` is bounded by `agent_response_lines`.
|
|
56
|
+
|
|
57
|
+
## Git contract
|
|
58
|
+
|
|
59
|
+
The agent should **not** run `git` itself — Forgeo handles it:
|
|
60
|
+
|
|
61
|
+
- Make changes in the repo.
|
|
62
|
+
- Do not `git add`/`commit`/`push`/`reset`.
|
|
63
|
+
- Exit `0` for commit, `no_changes_exit_code` for intentional no-op, `blocked_exit_code` for human input, anything else for discard.
|
|
64
|
+
|
|
65
|
+
Embed the contract in your prompt:
|
|
66
|
+
|
|
67
|
+
```yaml
|
|
68
|
+
agent_command: >
|
|
69
|
+
opencode run --auto "Work on the repository at the current working directory.
|
|
70
|
+
Make the requested changes and nothing else. Do NOT run git commit/push/add.
|
|
71
|
+
Verify with tests. Read AGENTS.md and CONTEXT.md if present; update them
|
|
72
|
+
if your change affects the overview.
|
|
73
|
+
$FORGEO_TASK"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Concurrency
|
|
77
|
+
|
|
78
|
+
One agent per Forgeo: `backlog.lock` prevents a second `start`/`once`, and `backlog.run` makes a waking daemon skip while a run is active.
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
# Backlog format
|
|
2
|
+
|
|
3
|
+
The backlog is a **plain JSON document** — a list of tasks Forgeo works through one by one. It can live as:
|
|
4
|
+
|
|
5
|
+
- [JSON file](#a-json-file-backlog) — local file
|
|
6
|
+
- [HTTP endpoint](#a-backlog-over-http) — another app serving the same document
|
|
7
|
+
- [Jira](#a-jira-backlog) / [GitHub](#a-github-backlog) / [GitLab](#a-gitlab-backlog) — native issues
|
|
8
|
+
|
|
9
|
+
## A JSON file backlog
|
|
10
|
+
|
|
11
|
+
Default is a file wherever `backlog:` points (`backlog.json` or `.forgeo/backlog.json` from `forgeo init`). Keep it outside the repo if possible.
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"tasks": [
|
|
16
|
+
{
|
|
17
|
+
"id": "TASK-001",
|
|
18
|
+
"title": "Implement fibonacci module",
|
|
19
|
+
"description": "Write a fibonacci module with memoization and tests.",
|
|
20
|
+
"status": "OPEN",
|
|
21
|
+
"created_at": "2026-07-31T10:00:00Z"
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Task schema
|
|
28
|
+
|
|
29
|
+
| Field | Type | Default | Description |
|
|
30
|
+
| --- | --- | --- | --- |
|
|
31
|
+
| `id` | string | — | Unique task ID. Duplicates rejected. |
|
|
32
|
+
| `title` | string | — | Short title (logs, commits, status). |
|
|
33
|
+
| `description` | string | — | Full instruction for the agent. Must be non-blank. |
|
|
34
|
+
| `status` | `OPEN`/`REVIEW`/`BLOCKED`/`COMPLETED`/`FAILED` | `OPEN` | Task state. |
|
|
35
|
+
| `created_at` | ISO-8601 | now (UTC) | Creation time; controls oldest-first order. |
|
|
36
|
+
| `updated_at` | ISO-8601 | now (UTC) | Bumped on status changes. |
|
|
37
|
+
| `run_at` | ISO-8601 / `null` | `null` | One-shot schedule — earliest pick time. Past = fire now; future = wait. |
|
|
38
|
+
| `dependencies` | string[] | `[]` | Task IDs that must be `COMPLETED` first (`REVIEW` not satisfied). |
|
|
39
|
+
| `acceptance_criteria` | string[] | `[]` | Rendered under "Acceptance criteria:" in `FORGEO_TASK`. |
|
|
40
|
+
| `files_to_modify` | string[] | `[]` | Hint for the agent. |
|
|
41
|
+
| `agent_command` | string / string[] | — | Per-task agent override. |
|
|
42
|
+
| `agent_timeout_seconds` | number | — | Per-task timeout override. |
|
|
43
|
+
| `blocker_reason` | string[] | `[]` | Engine-managed: reason when `BLOCKED`. |
|
|
44
|
+
| `blocked_count` | int | `0` | Engine-managed: times transitioned to `BLOCKED`. |
|
|
45
|
+
| `failure_reason` | string[] | `[]` | Engine-managed: error when `FAILED`. |
|
|
46
|
+
| `agent_response` | string / `null` | `null` | Engine-managed: agent output on transitions. |
|
|
47
|
+
| `retries_left` | int / `null` | `null` | Per-task retry budget override (`null` = config default, `0` = no retries). |
|
|
48
|
+
| `retry_count` | int | `0` | Engine-managed: retries already attempted. |
|
|
49
|
+
| `failed_wait_cycles` | int | `0` | Engine-managed: cycles waiting for retry. |
|
|
50
|
+
| `review_required` | bool / `null` | `null` | Per-task review override (`null` = inherit `review_mode`). |
|
|
51
|
+
| `review_branch` | string / `null` | `null` | Engine-managed: feature branch for `REVIEW`. |
|
|
52
|
+
| `review_commit_sha` | string / `null` | `null` | Engine-managed: commit SHA on review branch. |
|
|
53
|
+
|
|
54
|
+
Only `id`, `title`, `description` are required.
|
|
55
|
+
|
|
56
|
+
### Per-task agent routing
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
{
|
|
60
|
+
"tasks": [
|
|
61
|
+
{"id": "TASK-001", "title": "Add docstrings", "agent_command": "claude -p \"$FORGEO_TASK\" --model haiku"},
|
|
62
|
+
{"id": "TASK-002", "title": "Rearchitect cache", "agent_command": "claude -p \"$FORGEO_TASK\" --model opus"}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Statuses
|
|
68
|
+
|
|
69
|
+
| Status | Meaning |
|
|
70
|
+
| --- | --- |
|
|
71
|
+
| `OPEN` | To be picked. |
|
|
72
|
+
| `REVIEW` | Awaiting human review on feature branch; blocks dependants, not independent tasks. |
|
|
73
|
+
| `BLOCKED` | Needs human decision; Forgeo pauses. |
|
|
74
|
+
| `COMPLETED` | Agent finished, committed and pushed (or review approved). |
|
|
75
|
+
| `FAILED` | Agent errored; changes discarded, `failure_reason` recorded. |
|
|
76
|
+
|
|
77
|
+
### Review workflow
|
|
78
|
+
|
|
79
|
+
With `review_mode: branch` (and per-task `review_required` override) a successful task is committed on `review_branch_prefix + id` (default `forgeo/review/TASK-001`), pushed if `remote` set, and marked `REVIEW`. Independent `OPEN` tasks continue; dependants wait until `REVIEW` → `COMPLETED`.
|
|
80
|
+
|
|
81
|
+
Human merges the branch manually (PR or `git merge`), then marks **Complete** (`POST .../tasks/<id>/complete-review` → `COMPLETED`, clears `review_branch/sha`) or **Request changes** (`POST .../tasks/<id>/request-changes` → `OPEN`). `REVIEW` tasks are deletable and show `review_branch`/`review_commit_sha`. Issue providers use label `forgeo-review`.
|
|
82
|
+
|
|
83
|
+
### Retrying failed tasks
|
|
84
|
+
|
|
85
|
+
With `failed_retry_max` set, a `FAILED` task is moved back to `OPEN` after `failed_retry_wait_cycles` cycles and `retry_count` is incremented. Exhausted tasks stay `FAILED`. `BLOCKED` is never auto-retried.
|
|
86
|
+
|
|
87
|
+
Per-task override via `retries_left`:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{"id": "TASK-001", "title": "Flaky test", "retries_left": 0},
|
|
91
|
+
{"id": "TASK-002", "title": "Migrate cache", "retries_left": 3}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Retry state is visible in `runs.jsonl` and the dashboard (task card/modal, History **retry** column). Reopening a `FAILED` task manually (edit backlog or dashboard) resets `retry_count`.
|
|
95
|
+
|
|
96
|
+
### Resolving blocked tasks
|
|
97
|
+
|
|
98
|
+
On `BLOCKED`, Forgeo commits partial work as `[partial]` and records `blocker_reason`. `BLOCKER.md` is a derived view — re-rendered each cycle, auto-removed when no `BLOCKED` tasks remain.
|
|
99
|
+
|
|
100
|
+
Reopen via dashboard (**Reopen** button) or `POST /api/.../tasks/<id>/reopen` — clears `blocker_reason`, keeps `blocked_count`. Editing the file's `status` back to `OPEN` does *not* clear `blocker_reason`; prefer the Reopen action.
|
|
101
|
+
|
|
102
|
+
### Resolving review tasks
|
|
103
|
+
|
|
104
|
+
On `REVIEW`, code is on the feature branch. Merge manually, then **Complete** (`POST .../tasks/<id>/complete-review`) or **Request changes** (`POST .../tasks/<id>/request-changes` → `OPEN`). Webhook `review` event fires on entry to `REVIEW` when enabled.
|
|
105
|
+
|
|
106
|
+
## Picking order
|
|
107
|
+
|
|
108
|
+
Forgeo picks the **oldest runnable `OPEN` task** — smallest `created_at` whose dependencies are all `COMPLETED` (`REVIEW` does not satisfy) and whose `run_at` is not in the future.
|
|
109
|
+
|
|
110
|
+
- `run_at` in the **past** → picked before tasks without `run_at` (most overdue first); daemon wakes early for it.
|
|
111
|
+
- `run_at` in the **future** → skipped until that moment; daemon sleeps only until then.
|
|
112
|
+
- `null` / omitted → plain oldest-first.
|
|
113
|
+
- Dependencies: missing or non-`COMPLETED` dependencies keep the task waiting forever. Surfaced in `forgeo status` (`waiting on:`) and the dashboard's *Waiting on dependencies* banner.
|
|
114
|
+
|
|
115
|
+
## One-shot scheduling
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{"id": "TASK-001", "title": "Regenerate docs", "run_at": "2026-08-21T09:00:00Z"}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Use for time-sensitive work ("run after deploy"). Ignored for non-`OPEN` tasks or tasks with unmet dependencies. Set/clear via dashboard Create/Edit forms (`PATCH`).
|
|
122
|
+
|
|
123
|
+
## Dependencies
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{"id": "TASK-003", "title": "Deploy", "dependencies": ["TASK-001", "TASK-002"]}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Oldest-first among *runnable* tasks — if the oldest `OPEN` task is blocked, the next runnable one is picked. Cyclic or permanently blocked dependencies yield no runnable task → refactoring pass until resolved.
|
|
130
|
+
|
|
131
|
+
## Execution
|
|
132
|
+
|
|
133
|
+
Once picked, the task goes to the agent as `FORGEO_TASK`; exit code decides commit/push vs `BLOCKED` vs discarded. See [Agent contract](agent-contract.md).
|
|
134
|
+
|
|
135
|
+
## File backlog: corruption tolerance & snapshots
|
|
136
|
+
|
|
137
|
+
- Missing file → empty backlog (created on first write).
|
|
138
|
+
- Corrupt file → renamed to `backlog.json.corrupt-<timestamp>`, restored from newest valid snapshot or empty.
|
|
139
|
+
- Unparsable row → kept as `FAILED` placeholder, not a store failure.
|
|
140
|
+
- Before every agent run (and on daemon start), the backlog is copied to `backlog.json.bak` / `backlog.json.bak.1` (rotating, keeps last 2). On corrupt read, newest valid snapshot is restored. No snapshots for remote backlogs.
|
|
141
|
+
|
|
142
|
+
## A backlog over HTTP
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
backlog: https://api.example.com/backlog
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
| Operation | Request |
|
|
149
|
+
| --- | --- |
|
|
150
|
+
| Every read | `GET <url>` returns `{"tasks": [...]}` |
|
|
151
|
+
| Every write | `POST <url>` sends the full document |
|
|
152
|
+
|
|
153
|
+
Add `backlog_auth` for OAuth2. Same schema and ordering. Endpoint must:
|
|
154
|
+
|
|
155
|
+
- Return `{"tasks": [...]}`; non-object or non-list → empty.
|
|
156
|
+
- Replace, not append — POST body is the complete list.
|
|
157
|
+
- Send dates as ISO-8601 strings (not epoch numbers).
|
|
158
|
+
- Never send `null` for list fields.
|
|
159
|
+
- Preserve `agent_command` shape (string vs list).
|
|
160
|
+
- Store and return engine fields (`status`, `blocker_reason`, `failure_reason`, etc.).
|
|
161
|
+
|
|
162
|
+
A failed request fails the cycle (retried next interval), never treated as empty — otherwise a `POST` would overwrite the remote with nothing. Dashboard shows `502` for unreachable instances.
|
|
163
|
+
|
|
164
|
+
Runtime files (`backlog.lock`, `runs.jsonl`, etc.) go in `state_dir` (defaults to config dir).
|
|
165
|
+
|
|
166
|
+
## A Jira backlog
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
backlog_provider: jira
|
|
170
|
+
backlog: https://jira.example.com
|
|
171
|
+
jira:
|
|
172
|
+
jql: 'project = APP AND labels = forgeo'
|
|
173
|
+
project_key: APP
|
|
174
|
+
auth: {scheme: basic, username_env: JIRA_USER, token_env: JIRA_TOKEN}
|
|
175
|
+
workflow: {open_statuses: ["10000", "10001"], open_status: "10000", running_status: "3", completed_status: "10002"}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- Jira keys → task IDs; `summary`/`description`/`created`/`updated` → task fields.
|
|
179
|
+
- `open_statuses` = pickable; `running_status` = claimed before agent runs; `completed_status`/`blocked_status`/`failed_status` = terminal (blocked/failed optional — labels `forgeo-blocked`/`forgeo-failed` always applied).
|
|
180
|
+
- Engine state (`blocker_reason`, `retry_count`, `agent_response`, etc.) in Jira issue property `forgeo`.
|
|
181
|
+
- Optional `jira.fields` maps custom fields for `acceptance_criteria`, `dependencies`, etc. Without `run_at` mapping, Jira `duedate` is used at midnight UTC. Dependencies also inferred from `blocks` issue links.
|
|
182
|
+
- Auth from env vars: `basic` (username + token) or `bearer` (PAT). Uses REST API v3 (`/search/jql` + cursor) by default; set `api_version: 2` for older servers.
|
|
183
|
+
- Daemon paginates JQL; stale claims released after `claim_timeout_seconds`. Unavailable Jira fails the cycle.
|
|
184
|
+
|
|
185
|
+
## A GitHub backlog
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
backlog_provider: github
|
|
189
|
+
backlog: https://api.github.com # or https://github.example.com/api/v3
|
|
190
|
+
github: {repo: owner/repo, token_env: GITHUB_TOKEN}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
- Issue numbers → task IDs; `title`/`body`/`created_at`/`updated_at` → task fields.
|
|
194
|
+
- `open` → `OPEN`, `closed` → `COMPLETED`; labels `forgeo-running`/`blocked`/`failed` for the rest.
|
|
195
|
+
- Engine state in hidden `<!-- forgeo: {...} -->` block inside the body.
|
|
196
|
+
- Paginated `GET /repos/{owner}/{repo}/issues?state=all`; claiming adds `forgeo-running` + `claimed_at`.
|
|
197
|
+
|
|
198
|
+
Test live with `scripts/test-github-backlog-e2e.sh`:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
GITHUB_TOKEN=... ./scripts/test-github-backlog-e2e.sh
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## A GitLab backlog
|
|
205
|
+
|
|
206
|
+
```yaml
|
|
207
|
+
backlog_provider: gitlab
|
|
208
|
+
backlog: https://gitlab.example.com # instance root, /api/v4 appended
|
|
209
|
+
gitlab: {repo: group/project, token_env: GITLAB_TOKEN}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
- `iid` → task ID; `opened`/`closed` → `OPEN`/`COMPLETED`; same hidden-block and label mechanics as GitHub.
|
|
213
|
+
- Paginated `GET /api/v4/projects/:id/issues?state=all`.
|
|
214
|
+
|
|
215
|
+
## Managing tasks
|
|
216
|
+
|
|
217
|
+
Edit the file directly, or use the dashboard: **Create** form (`POST /api/instances/<name>/tasks` → auto `WEB-###` ID, `review_required` checkbox), task modal **Edit** (`PATCH` including `review_required`), **Delete** (`DELETE` for `OPEN`/`BLOCKED`/`REVIEW`), **Reopen** for `BLOCKED`, **Complete** / **Request changes** for `REVIEW`. For `jira`/`github`/`gitlab` the dashboard is a read-mostly mirror with links to native issues and banners to the native board.
|