hybrid-forge 0.2.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.
- hybrid_forge-0.2.0/LICENSE +201 -0
- hybrid_forge-0.2.0/PKG-INFO +470 -0
- hybrid_forge-0.2.0/README.md +441 -0
- hybrid_forge-0.2.0/forge/__init__.py +8 -0
- hybrid_forge-0.2.0/forge/__main__.py +6 -0
- hybrid_forge-0.2.0/forge/artifacts.py +186 -0
- hybrid_forge-0.2.0/forge/budget.py +286 -0
- hybrid_forge-0.2.0/forge/cli.py +2697 -0
- hybrid_forge-0.2.0/forge/config.py +1628 -0
- hybrid_forge-0.2.0/forge/evidence.py +672 -0
- hybrid_forge-0.2.0/forge/failures.py +1061 -0
- hybrid_forge-0.2.0/forge/imports.py +293 -0
- hybrid_forge-0.2.0/forge/ingest.py +882 -0
- hybrid_forge-0.2.0/forge/llama.py +497 -0
- hybrid_forge-0.2.0/forge/loop.py +8121 -0
- hybrid_forge-0.2.0/forge/manifests.py +211 -0
- hybrid_forge-0.2.0/forge/memory.py +1071 -0
- hybrid_forge-0.2.0/forge/patch.py +1033 -0
- hybrid_forge-0.2.0/forge/presets.py +172 -0
- hybrid_forge-0.2.0/forge/profile.py +164 -0
- hybrid_forge-0.2.0/forge/prompts.py +3447 -0
- hybrid_forge-0.2.0/forge/providers/__init__.py +175 -0
- hybrid_forge-0.2.0/forge/providers/_http.py +170 -0
- hybrid_forge-0.2.0/forge/providers/anthropic_api.py +251 -0
- hybrid_forge-0.2.0/forge/providers/base.py +668 -0
- hybrid_forge-0.2.0/forge/providers/claude_cli.py +307 -0
- hybrid_forge-0.2.0/forge/providers/gemini.py +166 -0
- hybrid_forge-0.2.0/forge/providers/llamacpp.py +536 -0
- hybrid_forge-0.2.0/forge/providers/openai_compat.py +434 -0
- hybrid_forge-0.2.0/forge/ratify.py +721 -0
- hybrid_forge-0.2.0/forge/replay.py +345 -0
- hybrid_forge-0.2.0/forge/repomap.py +143 -0
- hybrid_forge-0.2.0/forge/respec.py +1426 -0
- hybrid_forge-0.2.0/forge/routes.py +118 -0
- hybrid_forge-0.2.0/forge/secrets.py +69 -0
- hybrid_forge-0.2.0/forge/state.py +1815 -0
- hybrid_forge-0.2.0/forge/tokens.py +80 -0
- hybrid_forge-0.2.0/forge/toolchain.py +801 -0
- hybrid_forge-0.2.0/forge/tools.py +474 -0
- hybrid_forge-0.2.0/forge/ui/__init__.py +1 -0
- hybrid_forge-0.2.0/forge/ui/index.html +380 -0
- hybrid_forge-0.2.0/forge/ui/server.py +362 -0
- hybrid_forge-0.2.0/forge/wizard.py +805 -0
- hybrid_forge-0.2.0/hybrid_forge.egg-info/PKG-INFO +470 -0
- hybrid_forge-0.2.0/hybrid_forge.egg-info/SOURCES.txt +61 -0
- hybrid_forge-0.2.0/hybrid_forge.egg-info/dependency_links.txt +1 -0
- hybrid_forge-0.2.0/hybrid_forge.egg-info/entry_points.txt +2 -0
- hybrid_forge-0.2.0/hybrid_forge.egg-info/requires.txt +4 -0
- hybrid_forge-0.2.0/hybrid_forge.egg-info/top_level.txt +1 -0
- hybrid_forge-0.2.0/pyproject.toml +60 -0
- hybrid_forge-0.2.0/setup.cfg +4 -0
- hybrid_forge-0.2.0/tests/test_blind_grading.py +163 -0
- hybrid_forge-0.2.0/tests/test_blind_grading_defects.py +192 -0
- hybrid_forge-0.2.0/tests/test_forge.py +23084 -0
- hybrid_forge-0.2.0/tests/test_handback_ui.py +143 -0
- hybrid_forge-0.2.0/tests/test_handback_ui_render.py +54 -0
- hybrid_forge-0.2.0/tests/test_lint.py +112 -0
- hybrid_forge-0.2.0/tests/test_memory.py +716 -0
- hybrid_forge-0.2.0/tests/test_multimodal.py +692 -0
- hybrid_forge-0.2.0/tests/test_recorded_output.py +311 -0
- hybrid_forge-0.2.0/tests/test_sample_project.py +803 -0
- hybrid_forge-0.2.0/tests/test_tools.py +457 -0
- hybrid_forge-0.2.0/tests/test_wizard.py +867 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hybrid-forge
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Autonomous plan-and-execute coding loop over any models you bring.
|
|
5
|
+
Author: Nik
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/nschroeder8472/hybrid-forge
|
|
8
|
+
Project-URL: Repository, https://github.com/nschroeder8472/hybrid-forge
|
|
9
|
+
Project-URL: Issues, https://github.com/nschroeder8472/hybrid-forge/issues
|
|
10
|
+
Keywords: llm,agent,codegen,llama.cpp,claude,automation
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
21
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: flake8>=7; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# Hybrid Forge
|
|
31
|
+
|
|
32
|
+
An autonomous plan-and-execute coding loop. You define a feature, say go, and a
|
|
33
|
+
daemon runs the backlog to completion — planning, building, verifying, and
|
|
34
|
+
reviewing — pausing on its own when a usage window closes and resuming when it
|
|
35
|
+
reopens.
|
|
36
|
+
|
|
37
|
+
The point is not to replace Claude with a local model. It is to spend expensive
|
|
38
|
+
reasoning tokens on the parts that need reasoning, and let a cheaper model
|
|
39
|
+
absorb the token-heavy bulk generation for the cost of electricity.
|
|
40
|
+
|
|
41
|
+
## Measured throughput
|
|
42
|
+
|
|
43
|
+
Four runs against one repository, on two local models sharing a single 32 GB
|
|
44
|
+
card. Every figure is read from that project's `run.db`.
|
|
45
|
+
|
|
46
|
+
| Run | Backlog | Tickets | Attempts | Model calls | Tokens | Wall clock |
|
|
47
|
+
|---|---|---:|---:|---:|---:|---:|
|
|
48
|
+
| 1 | TypeScript port of a Godot level format | 9 | 18 | 169 | 3.00 M | 5 h 32 m |
|
|
49
|
+
| 2 | Canvas view for that format | 5 | 10 | 86 | 2.27 M | 2 h 00 m |
|
|
50
|
+
| 3 | Defects found reviewing run 2 | 3 | 3 | 54 | 1.13 M | 57 m |
|
|
51
|
+
| 4 | The one ticket run 3 could not place | 1 | 1 | 13 | 0.33 M | 15 m |
|
|
52
|
+
| | **Total** | **18** | **32** | **322** | **6.73 M** | **8 h 44 m** |
|
|
53
|
+
|
|
54
|
+
8 of the 18 tickets passed on their first attempt. The longest uninterrupted
|
|
55
|
+
run was 5 h 32 m.
|
|
56
|
+
|
|
57
|
+
Delivered by those runs and present in the repository now:
|
|
58
|
+
|
|
59
|
+
| | |
|
|
60
|
+
|---:|---|
|
|
61
|
+
| 34 | TypeScript files |
|
|
62
|
+
| 1,236 | lines of source |
|
|
63
|
+
| 2,213 | lines of tests |
|
|
64
|
+
| 184 | tests passing |
|
|
65
|
+
| 4 / 4 | verify commands exiting 0 (`lint`, `typecheck`, `typecheck:browser`, `test`) |
|
|
66
|
+
|
|
67
|
+
1 of those 34 files was edited by hand, between run 2 and run 3: 5 tests removed
|
|
68
|
+
from one file. They were there because run 2's spec put "the test command exits
|
|
69
|
+
0" on every ticket — a criterion the harness already settles — and the tester
|
|
70
|
+
encoded it the only way a criterion can be encoded, as tests that shell out to
|
|
71
|
+
run the commands. Reviewing run 2 found it, the tests were removed, and the rule
|
|
72
|
+
is now enforced where it was broken: the tester is told the harness runs those
|
|
73
|
+
commands, and `/forge-spec-check` reports the criterion at authoring time. Runs
|
|
74
|
+
3 and 4 were specified without it and needed no such edit.
|
|
75
|
+
|
|
76
|
+
Run 4 is the same shape one step further. Run 3 left one ticket parked because
|
|
77
|
+
its spec named no test file, so the tester's output landed outside the ticket's
|
|
78
|
+
own scope where the executor could not repair it — and because its criteria
|
|
79
|
+
asserted things about a DOM entry point that no test in the project can reach.
|
|
80
|
+
Both are now reported before a run starts, by `forge ingest` and by
|
|
81
|
+
`/forge-spec-check`. Respecified against what a test can actually assert, the
|
|
82
|
+
same work landed in one attempt.
|
|
83
|
+
|
|
84
|
+
Two earlier runs are excluded from the table. Both stopped for causes since
|
|
85
|
+
fixed, so neither describes what the loop does now:
|
|
86
|
+
[docs/CANVAS-POSTMORTEM.md](docs/CANVAS-POSTMORTEM.md).
|
|
87
|
+
|
|
88
|
+
## The loop is not a conversation
|
|
89
|
+
|
|
90
|
+
The orchestrator is a Python daemon that owns the state machine and reads its
|
|
91
|
+
next move from SQLite. No model decides what happens next.
|
|
92
|
+
|
|
93
|
+
That distinction is the whole design. A loop driven by a model inside a chat
|
|
94
|
+
session dies when the context window fills, when the process is killed, or when
|
|
95
|
+
a usage limit is hit at 2am. This one survives all three, because none of them
|
|
96
|
+
were holding the plan.
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
forged (daemon)
|
|
100
|
+
├─ state: .hybridforge/run.db
|
|
101
|
+
├─ RATIFY every role signs off on the ticket before it is built
|
|
102
|
+
├─ BUILD executor writes the implementation against the spec
|
|
103
|
+
├─ APPLY edits land on disk; anything outside scope is rejected
|
|
104
|
+
├─ TESTS tester encodes the ticket's criteria — never its own
|
|
105
|
+
├─ VERIFY lint / typecheck / test, before any model reviews
|
|
106
|
+
├─ REVIEW reviewer reads the diff against the spec
|
|
107
|
+
├─ RECORD durable outcomes to memory (opt-in, usually nothing)
|
|
108
|
+
└─ COMMIT optional
|
|
109
|
+
loop until DONE | BLOCKED | stopped
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Diagrams of the loop, the two ways work enters it, and what differs between a
|
|
113
|
+
greenfield repo and an existing one: [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
|
114
|
+
|
|
115
|
+
## Bring your own models
|
|
116
|
+
|
|
117
|
+
Four roles — `planner`, `executor`, `tester`, `reviewer` — and any model can play
|
|
118
|
+
any of them. Local models are llama.cpp; cloud is whichever vendor you have:
|
|
119
|
+
|
|
120
|
+
| kind | reaches |
|
|
121
|
+
|---|---|
|
|
122
|
+
| `llamacpp` | **the local backend.** `llama-server` in router mode, swapping checkpoints on one endpoint as the loop alternates roles |
|
|
123
|
+
| `openai` | OpenAI, and the gateways that speak its wire — OpenRouter, LiteLLM, Together, DeepSeek |
|
|
124
|
+
| `anthropic` | Anthropic Messages API |
|
|
125
|
+
| `gemini` | Google Gemini |
|
|
126
|
+
| `claude-cli` | headless `claude -p`, so planning and review run on an existing Claude subscription rather than an API key |
|
|
127
|
+
|
|
128
|
+
One local backend is deliberate. Forge used to carry four — Ollama and friends
|
|
129
|
+
through `openai`, FreeToken, and a `command` escape hatch — and each had its own
|
|
130
|
+
way of being asked what it was serving, its own way of being told to load
|
|
131
|
+
something else, and its own silent failure. An Ollama name that never matched
|
|
132
|
+
because config omitted `:latest`; an engine that answered to any model id and
|
|
133
|
+
echoed it back. Every one of them cost a run before it was found, and carrying
|
|
134
|
+
them meant diagnostics could only say what all four had in common.
|
|
135
|
+
|
|
136
|
+
With one, they can name the thing in front of you: the preset a checkpoint is
|
|
137
|
+
spawned from, the `--models-max` slot another model is holding, the reasoning
|
|
138
|
+
budget a thinking model is spending its whole answer on, the context window out
|
|
139
|
+
of the argv rather than out of a guess. `forge models` writes the preset from
|
|
140
|
+
your config, so the numbers in the two files cannot drift apart.
|
|
141
|
+
|
|
142
|
+
It also means forge can install the backend rather than describe it. `forge llama
|
|
143
|
+
install` fetches a pinned llama.cpp build, picks CUDA over Vulkan by reading the
|
|
144
|
+
GPU's compute capability, and verifies the download against the SHA-256 GitHub
|
|
145
|
+
published for it before unpacking. The same checkpoint measured 16 tok/s on a
|
|
146
|
+
Vulkan build and 353 on CUDA, and nothing reports the slow path — see
|
|
147
|
+
[docs/LLAMA-PACKAGING.md](docs/LLAMA-PACKAGING.md).
|
|
148
|
+
|
|
149
|
+
### Two models that work
|
|
150
|
+
|
|
151
|
+
A pairing that has run this loop end to end, on a single 32 GB card. It is one
|
|
152
|
+
machine and one project rather than a benchmark, so take it as a starting point
|
|
153
|
+
that is known to work rather than as the answer:
|
|
154
|
+
|
|
155
|
+
| Role | Checkpoint | Why this one |
|
|
156
|
+
|---|---|---|
|
|
157
|
+
| `planner`, `reviewer` | **Nemotron-3-Nano-Omni-30B-A3B-Reasoning**, Q4_K_M | An A3B MoE: 30B of weights, ~3B active, so it reads a long ticket and a long diff at around 150 tok/s. Planning and review are the roles that read the most and write the least, which is exactly what a sparse model is cheap at |
|
|
158
|
+
| `executor`, `tester` | **Qwen3.8-27B**, UD-Q4_K_M | Dense, ~40 tok/s, and the executor emits whole files — the role where being right per token beats being fast per token. It is the half of the run worth spending the slower model on |
|
|
159
|
+
|
|
160
|
+
Both at `ctx-size = 131072`, both `exclusive`, `--models-max 1`. They do not
|
|
161
|
+
co-reside: about 25 GiB resident each against 32 GB of VRAM, so the router
|
|
162
|
+
swaps checkpoints as the loop alternates roles, and that swap is why
|
|
163
|
+
`loop.ratifyOrder` is worth grouping by model — see `/forge-setup`.
|
|
164
|
+
|
|
165
|
+
One run, for scale: five tickets, all landing, 120 minutes, 86 model calls and
|
|
166
|
+
2.27M tokens — 1.10M in and 415K out through the executor pair, 545K in and
|
|
167
|
+
210K out through the planner pair. All of it local, on electricity.
|
|
168
|
+
|
|
169
|
+
**Set `reasoningBudget` on both, before the first run.** Neither of these is
|
|
170
|
+
usable without it. Unbounded, the Nemotron spent an entire 32,768-token output
|
|
171
|
+
budget on hidden reasoning and never began its answer — forge notices, throws
|
|
172
|
+
the call away and retries with thinking off, at a cost of roughly 93 seconds of
|
|
173
|
+
wasted generation per planner and reviewer call. 8,192 for the Nemotron and
|
|
174
|
+
6,144 for the Qwen leave both room to answer. The budget is one number per
|
|
175
|
+
checkpoint, so size it against the *smaller* of the two output budgets the
|
|
176
|
+
roles sharing it are given.
|
|
177
|
+
|
|
178
|
+
Two smaller things that cost a run each if missed. Nemotron's "Omni" is
|
|
179
|
+
multimodal, and a preset generated from `--models-dir` loads the vision
|
|
180
|
+
projector beside a text-only checkpoint, spending VRAM no role here uses —
|
|
181
|
+
`no-mmproj = true`. And the Qwen returns its `<think>` block inline in
|
|
182
|
+
`content` rather than in `reasoning_content` depending on the chat template;
|
|
183
|
+
forge strips it at the provider boundary, which is why it is worth knowing that
|
|
184
|
+
a reply looking truncated in the logs may have been trimmed there.
|
|
185
|
+
|
|
186
|
+
Adding a backend is one module and one registry line. Nothing in the loop, the
|
|
187
|
+
budget gate, or the dashboard knows which kind it is talking to.
|
|
188
|
+
|
|
189
|
+
## Waiting is a feature
|
|
190
|
+
|
|
191
|
+
Subscription plans enforce a rolling usage window, and when it is exhausted the
|
|
192
|
+
Claude CLI does not return a `429` with a header — it prints a sentence. The
|
|
193
|
+
budget gate parses that into a reset time and parks the run in
|
|
194
|
+
`waiting_budget`, which is a live state, not a failure: the dashboard shows when
|
|
195
|
+
the window reopens and the loop wakes itself up.
|
|
196
|
+
|
|
197
|
+
The same gate checks context windows before a call rather than after, so a
|
|
198
|
+
prompt that will not fit is trimmed or the ticket is flagged for splitting —
|
|
199
|
+
instead of the backend silently truncating an implementation.
|
|
200
|
+
|
|
201
|
+
## Project memory reaches the executor
|
|
202
|
+
|
|
203
|
+
The daemon retrieves prior decisions from MemPalace before each ticket and
|
|
204
|
+
passes them to both the executor and the reviewer, so a local model does not
|
|
205
|
+
relitigate a convention settled weeks ago. It speaks MCP directly — no Claude
|
|
206
|
+
Code in the path — and discovers the server's tool surface at connect time
|
|
207
|
+
rather than hardcoding names that shift between MemPalace versions.
|
|
208
|
+
|
|
209
|
+
Retrieved context is the *droppable* part of a prompt: when a ticket does not
|
|
210
|
+
fit the executor's context window, memory yields and the spec stays. A memory
|
|
211
|
+
outage degrades the run to "no context", never ends it.
|
|
212
|
+
|
|
213
|
+
Write-back is the other direction, and it is opt-in. After a ticket passes
|
|
214
|
+
review the loop can ask whether the work settled a decision or produced a
|
|
215
|
+
correction worth keeping, and record it. Off by default, because retrieval only
|
|
216
|
+
reads while recording mutates a store every future session reads back with no
|
|
217
|
+
undo — and because a memory full of ticket narration is worse than an empty
|
|
218
|
+
one. `dryRun` logs what it would write without writing it. Entries that match a
|
|
219
|
+
credential shape are refused before any network call, and destructive tools are
|
|
220
|
+
never selected.
|
|
221
|
+
|
|
222
|
+
## Where each piece runs
|
|
223
|
+
|
|
224
|
+
**The daemon goes where the project goes.** It writes the executor's files, runs
|
|
225
|
+
your lint and test commands, and builds the diff the reviewer reads — all
|
|
226
|
+
against a local working tree. It is not a compute service you can point at a
|
|
227
|
+
repo across the network.
|
|
228
|
+
|
|
229
|
+
So the split is by weight, not by role:
|
|
230
|
+
|
|
231
|
+
| Machine | Runs |
|
|
232
|
+
|---|---|
|
|
233
|
+
| GPU host | `llama-server` in router mode, serving the local models; MemPalace |
|
|
234
|
+
| Your workstation | the repo, the daemon, Claude Code, the toolchain |
|
|
235
|
+
|
|
236
|
+
Model calls and memory reads cross the network; files, git, and builds stay
|
|
237
|
+
local. Any network the two machines share will do — the only requirement is
|
|
238
|
+
that the daemon can reach the model and memory endpoints, and that those
|
|
239
|
+
endpoints are not reachable by anything else, since neither has authentication
|
|
240
|
+
of its own. This is also the only workable shape for a macOS-only
|
|
241
|
+
toolchain — `xcodebuild` and the simulators cannot run in a Linux container, so
|
|
242
|
+
verification has to happen on the Mac.
|
|
243
|
+
|
|
244
|
+
The daemon is stdlib-only Python, so "install it next to the project" is
|
|
245
|
+
`pip install -e .`, not a deployment.
|
|
246
|
+
|
|
247
|
+
**Containerizing is for the verify step, not the daemon.** `commands` are
|
|
248
|
+
ordinary shell strings, so isolating model-authored code from your host needs
|
|
249
|
+
no special support:
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
"commands": {
|
|
253
|
+
"test": "docker run --rm -v \"/abs/path/repo\":/w -w /w python:3.12-slim python -m pytest -q"
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
That is worth doing on its own merits — today those commands run directly on
|
|
258
|
+
your machine, and they are running code a model just wrote.
|
|
259
|
+
|
|
260
|
+
## Start from a plan written anywhere
|
|
261
|
+
|
|
262
|
+
The loop's input does not have to come from this tool:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
forge ingest plan.md # a spec from the Claude app, a web chat, or a human
|
|
266
|
+
forge go
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
If the document already contains ticket-shaped sections they are used verbatim —
|
|
270
|
+
no model re-reads them, and the acceptance criteria stay the ones their author
|
|
271
|
+
wrote. Only freeform documents go through the planner.
|
|
272
|
+
|
|
273
|
+
## What it deliberately does not do
|
|
274
|
+
|
|
275
|
+
**The executor never decides scope.** It gets an explicit spec, an allowed file
|
|
276
|
+
list, and acceptance criteria. Every edit is checked against that list before it
|
|
277
|
+
touches disk, and paths that escape the project root are refused outright.
|
|
278
|
+
|
|
279
|
+
**The executor never authors its own acceptance criteria.** A model that writes
|
|
280
|
+
both the implementation and the test it is judged against will encode its bugs
|
|
281
|
+
as passing tests.
|
|
282
|
+
|
|
283
|
+
**Triage is not delegated.** A ticket routed `withheld:<reason>` is left for a
|
|
284
|
+
human even if that stalls the backlog. The reason travels in the route —
|
|
285
|
+
`withheld:security`, `withheld:concurrency`, `withheld:interface` — so a ticket
|
|
286
|
+
parked in March still says in May what it was parked for. Auth, concurrency,
|
|
287
|
+
migrations, and public API surface stay with a person.
|
|
288
|
+
|
|
289
|
+
**A bug is never fixed on faith.** `forge bug` writes a test that asserts the
|
|
290
|
+
correct behavior and requires it to *fail* before any fix is attempted. A fault
|
|
291
|
+
that cannot be demonstrated parks for a human rather than being fixed against a
|
|
292
|
+
guess — see [docs/BUG-LOOP.md](docs/BUG-LOOP.md).
|
|
293
|
+
|
|
294
|
+
**A `BLOCKED:` never retries.** An underspecified spec does not improve by being
|
|
295
|
+
asked again — the run parks the ticket and says what was ambiguous.
|
|
296
|
+
|
|
297
|
+
## Monitoring
|
|
298
|
+
|
|
299
|
+
`forge go` serves a dashboard on `127.0.0.1:8799`: backlog with per-ticket
|
|
300
|
+
status, live event stream, recent steps, per-model token usage, and
|
|
301
|
+
pause/resume/stop. It reads the same SQLite the loop writes, so a crashed
|
|
302
|
+
dashboard cannot take a run with it and a restarted one reattaches with no
|
|
303
|
+
handshake.
|
|
304
|
+
|
|
305
|
+
It has no authentication and its stop button ends a run, so it binds to
|
|
306
|
+
loopback and warns on startup if you point `ui.host` anywhere else. Tunnel in
|
|
307
|
+
rather than widening the bind address, or put something that authenticates in
|
|
308
|
+
front of it.
|
|
309
|
+
|
|
310
|
+
## Commands
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
forge init [--defaults] # set up .hybridforge/ for this repo, with prompts
|
|
314
|
+
forge doctor # probe every configured model
|
|
315
|
+
forge ingest <file|-> # spec or plan -> reviewable backlog
|
|
316
|
+
forge go [--plan f] [--open]# run until done or stopped
|
|
317
|
+
forge go --retries N # requeue and respec what did not land, N more
|
|
318
|
+
# times; -1 = until clean or stopped
|
|
319
|
+
forge status # one-shot summary
|
|
320
|
+
forge retry [--respec] # requeue failed tickets, optionally re-specced
|
|
321
|
+
forge bug "<report>" # reproduce a bug, then fix it
|
|
322
|
+
forge toolchain # what tests each language; set up what nothing does
|
|
323
|
+
forge criteria [ID --accept N]
|
|
324
|
+
# adopt a criterion the loop proposed and refused
|
|
325
|
+
forge advise <ID> "<note>" # a note the planner and executor read next pass
|
|
326
|
+
forge release <ID> "<why>" # hand a withheld ticket back to the executor
|
|
327
|
+
forge discharge <ID> # mark a withheld ticket done — you wrote the code
|
|
328
|
+
forge models # write the llama.cpp preset from your config
|
|
329
|
+
forge llama [status|install|list]
|
|
330
|
+
# fetch or inspect the pinned llama.cpp build
|
|
331
|
+
forge replay # re-read a past run's output with today's parsers
|
|
332
|
+
forge prune # delete the artifact trees of old runs
|
|
333
|
+
forge pause | resume | stop # applied after the current step, never mid-patch
|
|
334
|
+
forge ui # dashboard without running the loop
|
|
335
|
+
forge ui --host IP --port N # bind it elsewhere, this run only (no auth!)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
`advise`, `release` and `discharge` are the return channel: a run that parks a
|
|
339
|
+
ticket can be answered without restarting it, and a ticket you implemented by
|
|
340
|
+
hand can be closed without pretending the loop did it. See
|
|
341
|
+
[docs/HANDBACK.md](docs/HANDBACK.md).
|
|
342
|
+
|
|
343
|
+
Two Claude Code plugins sit beside the CLI rather than wrapping it. **Forge
|
|
344
|
+
Setup** (`/forge-setup`) handles the cold start — install check, endpoint
|
|
345
|
+
probes, the machine profile, and this repo's verify commands and never-delegate
|
|
346
|
+
list. **Forge Spec** (`/forge-spec`, `/forge-spec-check`) is where a feature
|
|
347
|
+
gets designed into a document `forge ingest` parses verbatim, so the acceptance
|
|
348
|
+
criteria stay in the words a human wrote. Running the loop stays in the
|
|
349
|
+
terminal, where it survives the session.
|
|
350
|
+
|
|
351
|
+
## Setup asks once
|
|
352
|
+
|
|
353
|
+
`forge init` prompts for the endpoints, **probes each one while you are still
|
|
354
|
+
sitting there**, and writes what it learned to a machine-level profile
|
|
355
|
+
(`~/.config/hybrid-forge/profile.json`, `%APPDATA%\hybrid-forge\` on Windows).
|
|
356
|
+
The next repo starts from those answers, so the second setup is Enter-through
|
|
357
|
+
except for the things that repo actually decides.
|
|
358
|
+
|
|
359
|
+
A wrong endpoint found now costs one retyped line. The same wrong endpoint found
|
|
360
|
+
by `forge go` costs the run.
|
|
361
|
+
|
|
362
|
+
For the verify commands it does not guess at all. It collects the repo's own CI
|
|
363
|
+
workflow, Makefile, and contributing guide, hands them to the planner model, and
|
|
364
|
+
asks what this project actually runs — so you get `cargo nextest run --workspace`
|
|
365
|
+
because that is what CI runs, not `cargo test` because a `Cargo.toml` exists.
|
|
366
|
+
Nothing found means an empty field, which the loop skips. A wrong `test` command
|
|
367
|
+
does not fail once; it fails `maxAttempts` times per ticket and parks the whole
|
|
368
|
+
backlog, looking exactly like a bad executor model.
|
|
369
|
+
|
|
370
|
+
Credentials are never stored. Providers resolve keys through `apiKeyEnv`, the
|
|
371
|
+
*name* of an environment variable, and that name is what the profile keeps.
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
forge init # prompts, probes, remembers
|
|
375
|
+
forge init --defaults # no questions; writes a config to edit by hand
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
With no terminal attached — piped, redirected, or run from a script — it takes
|
|
379
|
+
every default and says so rather than blocking on stdin nobody is watching.
|
|
380
|
+
|
|
381
|
+
First time through, [docs/QUICKSTART.md](docs/QUICKSTART.md) walks the whole
|
|
382
|
+
setup — llama.cpp and which models to run, MemPalace, the daemon, and a narrated
|
|
383
|
+
first `forge init`. [docs/SETUP.md](docs/SETUP.md) is the reference behind it:
|
|
384
|
+
every option, every alternative, and the full security discussion. The daemon is
|
|
385
|
+
stdlib-only Python 3.10+ — a failed `pip install` is a bad way to discover that
|
|
386
|
+
an overnight run never started.
|
|
387
|
+
|
|
388
|
+
[docs/BUG-LOOP.md](docs/BUG-LOOP.md) covers `forge bug` — the loop that has to
|
|
389
|
+
reproduce a fault before it is allowed to fix it, and what it refuses to do when
|
|
390
|
+
it cannot.
|
|
391
|
+
|
|
392
|
+
[docs/CANVAS-POSTMORTEM.md](docs/CANVAS-POSTMORTEM.md) is the shortest way to
|
|
393
|
+
see what this loop's failures actually look like: a backlog that parked without
|
|
394
|
+
writing a line because the parser dropped three fifths of its criteria, and the
|
|
395
|
+
run after it that went green while deleting the project's dependencies.
|
|
396
|
+
|
|
397
|
+
[docs/LOOP-INVARIANTS.md](docs/LOOP-INVARIANTS.md) is the one to read before
|
|
398
|
+
adding a step, a role, or any check that attributes blame. Eighteen rules that
|
|
399
|
+
hold across the whole harness — read scope versus write scope, why an anchor the loop
|
|
400
|
+
wrote is not an anchor, why attribution must come from diagnostic blocks and
|
|
401
|
+
never from raw output. Each was learned by breaking it.
|
|
402
|
+
|
|
403
|
+
[docs/CONFIG.md](docs/CONFIG.md) is the key-by-key reference for
|
|
404
|
+
`.hybridforge/config.json`, with a populated example at
|
|
405
|
+
[templates/config.sample.json](templates/config.sample.json) to copy from.
|
|
406
|
+
|
|
407
|
+
[docs/ROADMAP.md](docs/ROADMAP.md) holds what is not built yet and why — the
|
|
408
|
+
bug-report loop first among it.
|
|
409
|
+
|
|
410
|
+
## Layout
|
|
411
|
+
|
|
412
|
+
```
|
|
413
|
+
forge/providers/ adapter layer — one module per backend
|
|
414
|
+
forge/loop.py the state machine
|
|
415
|
+
forge/budget.py context accounting + rate-limit gate
|
|
416
|
+
forge/state.py SQLite: runs, tickets, steps, events, usage, control
|
|
417
|
+
forge/memory.py MCP client for project memory (read + guarded write)
|
|
418
|
+
forge/secrets.py credential detection for anything about to be persisted
|
|
419
|
+
forge/ingest.py outside spec/plan -> backlog
|
|
420
|
+
forge/patch.py model output -> file writes, with scope enforcement
|
|
421
|
+
forge/manifests.py what a build manifest declared before a rewrite
|
|
422
|
+
forge/prompts.py per-role prompts (a contract the parsers depend on)
|
|
423
|
+
forge/ratify.py the pre-build sign-off pass
|
|
424
|
+
forge/respec.py revising a ticket from why it failed
|
|
425
|
+
forge/routes.py delegate vs withheld:<reason>, and what withheld means
|
|
426
|
+
forge/evidence.py which files a ticket may read
|
|
427
|
+
forge/llama.py fetching and verifying the pinned llama.cpp build
|
|
428
|
+
forge/presets.py config -> the llama.cpp router preset
|
|
429
|
+
forge/wizard.py interactive `forge init` — asks, probes, never hangs
|
|
430
|
+
forge/toolchain.py reads the repo's CI/docs to find its verify commands
|
|
431
|
+
forge/profile.py machine-level endpoints, reused by the next repo
|
|
432
|
+
forge/ui/ dashboard
|
|
433
|
+
plugins/forge-setup/ Claude Code plugin: machine + repository setup
|
|
434
|
+
plugins/forge-spec/ Claude Code plugin: spec authoring, triage, memory
|
|
435
|
+
examples/sample-project/ the fixture a loop change is run against
|
|
436
|
+
scripts/sample_workspace.py copies that fixture somewhere a run may write
|
|
437
|
+
tests/ python -m unittest discover tests
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Changing the loop
|
|
441
|
+
|
|
442
|
+
Unit tests say a change is what you meant. They do not say what it does to a
|
|
443
|
+
run, and most of what this project knows came from watching a real backlog
|
|
444
|
+
fail. `examples/sample-project` is the cheapest imitation of that: two builds,
|
|
445
|
+
a three-ticket spec on the parsed path, one dependency between tickets, a green
|
|
446
|
+
baseline, and a bug the suite does not catch.
|
|
447
|
+
|
|
448
|
+
```
|
|
449
|
+
python scripts/sample_workspace.py # copy it somewhere a run may write
|
|
450
|
+
cd <the path it prints>
|
|
451
|
+
forge --root . doctor # the coverage matrix, no tokens spent
|
|
452
|
+
forge ingest SPEC.md # three tickets, parsed
|
|
453
|
+
forge go
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Run it against a copy, never in place — a run writes code, a database and an
|
|
457
|
+
artifact tree, and the committed tree is a fixture. `.gitignore` holds the
|
|
458
|
+
fixture as an allow-list so anything a run leaves behind is ignored rather than
|
|
459
|
+
staged, and `tests/test_sample_project.py` pins what a run depends on: both
|
|
460
|
+
suites green, the spec parsed rather than replanned, every path it names owned
|
|
461
|
+
by a build, every ticket carrying its own test file, the four paths the spec
|
|
462
|
+
must find missing, and the seeded defect still a defect. Those run with the
|
|
463
|
+
ordinary suite and spend nothing.
|
|
464
|
+
|
|
465
|
+
## Status
|
|
466
|
+
|
|
467
|
+
Working end to end, and young. Run it on a low-stakes slice first. The failure
|
|
468
|
+
mode to watch for is not "the code doesn't compile" — it is plausible code that
|
|
469
|
+
quietly does the wrong thing, which is what the review step and the scope
|
|
470
|
+
checks exist to catch.
|