tasks-toolkit 0.1.3__py3-none-any.whl
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.
- scripts/__init__.py +0 -0
- scripts/agents_sync.py +61 -0
- scripts/apply-plan.sh +51 -0
- scripts/build.sh +7 -0
- scripts/codex-task.sh +198 -0
- scripts/install-hooks.sh +9 -0
- scripts/junie-status.sh +52 -0
- scripts/junie-task.sh +183 -0
- scripts/kilo-task.sh +226 -0
- scripts/needs-dotnet-checks.sh +24 -0
- scripts/plan-sprint.sh +92 -0
- scripts/run-task.sh +79 -0
- scripts/run.sh +7 -0
- scripts/run_stats.py +141 -0
- scripts/run_summary.py +94 -0
- scripts/spec-lint.sh +139 -0
- scripts/sprint-auto.sh +79 -0
- scripts/sprint_auto_execute.py +160 -0
- scripts/sprint_auto_preflight.py +309 -0
- scripts/sprint_auto_prepare.py +271 -0
- scripts/sprint_auto_resume.py +153 -0
- scripts/sprint_auto_state.py +191 -0
- scripts/sprint_plan.py +238 -0
- scripts/task-report.sh +45 -0
- scripts/tasks.py +1813 -0
- scripts/tasks_cli.py +14 -0
- scripts/tasks_commands.py +11 -0
- scripts/tasks_init.py +140 -0
- scripts/tasks_mcp.py +309 -0
- scripts/tasks_model.py +173 -0
- scripts/tasks_plan_import.py +269 -0
- scripts/tasks_render.py +148 -0
- scripts/tasks_storage.py +373 -0
- scripts/tasks_watch.py +159 -0
- scripts/test.sh +43 -0
- scripts/test_agent_config.py +97 -0
- scripts/test_plan_sprint.sh +172 -0
- scripts/test_python_fast.sh +26 -0
- scripts/test_python_lint.sh +22 -0
- scripts/test_python_slow.sh +14 -0
- scripts/test_run_stats.py +64 -0
- scripts/test_runners.sh +588 -0
- scripts/test_spec_lint.sh +438 -0
- scripts/test_sprint_auto.py +50 -0
- scripts/test_sprint_auto_execute.py +112 -0
- scripts/test_sprint_auto_preflight.py +143 -0
- scripts/test_sprint_auto_prepare.py +150 -0
- scripts/test_sprint_auto_resume.py +104 -0
- scripts/test_sprint_auto_state.py +157 -0
- scripts/test_tasks.py +1678 -0
- scripts/test_tasks_mcp.py +233 -0
- scripts/test_tasks_plan_import.py +129 -0
- scripts/test_tasks_storage.py +67 -0
- scripts/test_tasks_watch.py +239 -0
- scripts/test_worktree.sh +2112 -0
- scripts/worktree.sh +992 -0
- scripts/worktree_mr.py +133 -0
- scripts/worktree_path.py +70 -0
- scripts/worktree_sprint.py +70 -0
- tasks_toolkit-0.1.3.dist-info/METADATA +121 -0
- tasks_toolkit-0.1.3.dist-info/RECORD +65 -0
- tasks_toolkit-0.1.3.dist-info/WHEEL +5 -0
- tasks_toolkit-0.1.3.dist-info/entry_points.txt +4 -0
- tasks_toolkit-0.1.3.dist-info/licenses/LICENSE +21 -0
- tasks_toolkit-0.1.3.dist-info/top_level.txt +1 -0
scripts/test_runners.sh
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Tests for the delegation runners' model recording (junie-task.sh / kilo-task.sh):
|
|
3
|
+
# every status line carries model=/runner=, the final stdout report has runner: and
|
|
4
|
+
# model: header lines, tmp/runs/<name>.summary.json is written with the right verdict,
|
|
5
|
+
# and junie-status.sh reports runner/model from the summary (or the last status line).
|
|
6
|
+
# Plain bash, no external dependencies beyond git, python3 and grep. Each test runs in
|
|
7
|
+
# its own temporary git repo with a fake `junie`/`kilo` executable first in PATH that
|
|
8
|
+
# edits a file and exits 0 or non-zero (see make_repo). Prints PASS/FAIL per case.
|
|
9
|
+
# Exit code: 0 if all pass, 1 if any fail.
|
|
10
|
+
set -uo pipefail
|
|
11
|
+
|
|
12
|
+
passed=0
|
|
13
|
+
failed=0
|
|
14
|
+
total=0
|
|
15
|
+
|
|
16
|
+
pass() { passed=$((passed + 1)); total=$((total + 1)); echo " PASS: $1"; }
|
|
17
|
+
fail() { failed=$((failed + 1)); total=$((total + 1)); echo " FAIL: $1 — $2"; }
|
|
18
|
+
|
|
19
|
+
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
20
|
+
|
|
21
|
+
# ── helpers ──────────────────────────────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
# make_repo <name>: build a throw-away git repo with the runner scripts installed,
|
|
24
|
+
# a fake junie/kilo in bin/ (first in PATH), a fake task-report.sh that logs its
|
|
25
|
+
# arguments (so tracker notes are assertable) and a ready "worktree" for <name>.
|
|
26
|
+
make_repo() {
|
|
27
|
+
local name="$1"
|
|
28
|
+
TEST_REPO="$(mktemp -d)"
|
|
29
|
+
cd "$TEST_REPO"
|
|
30
|
+
git init -q
|
|
31
|
+
git config user.name "test"
|
|
32
|
+
git config user.email "test@localhost"
|
|
33
|
+
mkdir -p scripts tmp/worktrees/"$name" tmp/meta tmp/runs tmp/patches tmp/xdg-empty bin
|
|
34
|
+
for f in worktree.sh junie-task.sh kilo-task.sh codex-task.sh run-task.sh junie-status.sh run_summary.py spec-lint.sh; do
|
|
35
|
+
cp "$PROJECT_ROOT/scripts/$f" "scripts/$f"
|
|
36
|
+
done
|
|
37
|
+
mkdir -p agents/junie agents/kilo scripts/coder-git-hooks
|
|
38
|
+
cp "$PROJECT_ROOT/agents/junie/RUN.md" agents/junie/RUN.md
|
|
39
|
+
cp "$PROJECT_ROOT/agents/kilo/RUN.md" agents/kilo/RUN.md
|
|
40
|
+
cp "$PROJECT_ROOT/scripts/coder-git-hooks/prepare-commit-msg" scripts/coder-git-hooks/prepare-commit-msg
|
|
41
|
+
# fake task-report.sh: record every call so the model= in tracker notes is assertable
|
|
42
|
+
cat > scripts/task-report.sh <<'FAKE'
|
|
43
|
+
#!/usr/bin/env bash
|
|
44
|
+
printf '%s\n' "$*" >> "${TASK_REPORT_LOG:?}"
|
|
45
|
+
exit 0
|
|
46
|
+
FAKE
|
|
47
|
+
chmod +x scripts/worktree.sh scripts/junie-task.sh scripts/kilo-task.sh scripts/run-task.sh scripts/junie-status.sh scripts/task-report.sh scripts/coder-git-hooks/prepare-commit-msg
|
|
48
|
+
# Runner artifacts live under tmp/ and must not leak into the agent patch;
|
|
49
|
+
# tmp/worktrees/ stays visible so the fake agent's edits are stageable.
|
|
50
|
+
cat > .gitignore <<'EOF'
|
|
51
|
+
tmp/runs/
|
|
52
|
+
tmp/meta/
|
|
53
|
+
tmp/patches/
|
|
54
|
+
tmp/xdg-empty/
|
|
55
|
+
EOF
|
|
56
|
+
cat > bin/junie <<'FAKE'
|
|
57
|
+
#!/usr/bin/env bash
|
|
58
|
+
# fake agent for tests: writes solution.txt into the agent worktree (given via
|
|
59
|
+
# --project=<dir> or --dir <dir>), then exits 3/5 if the prompt carries EXIT_3/EXIT_5.
|
|
60
|
+
set -u
|
|
61
|
+
wt=""
|
|
62
|
+
shift_wt=0
|
|
63
|
+
for a in "$@"; do
|
|
64
|
+
case "$a" in
|
|
65
|
+
--project=*) wt="${a#--project=}" ;;
|
|
66
|
+
--dir=*) wt="${a#--dir=}" ;;
|
|
67
|
+
--dir) shift_wt=1 ;;
|
|
68
|
+
*)
|
|
69
|
+
if [ "$shift_wt" = "1" ]; then
|
|
70
|
+
wt="$a"
|
|
71
|
+
shift_wt=0
|
|
72
|
+
fi
|
|
73
|
+
;;
|
|
74
|
+
esac
|
|
75
|
+
done
|
|
76
|
+
[ -n "$wt" ] || exit 9
|
|
77
|
+
if [ -n "${FAKE_KILO_ARGS_LOG:-}" ]; then
|
|
78
|
+
printf '%s\n' "$*" > "$FAKE_KILO_ARGS_LOG"
|
|
79
|
+
fi
|
|
80
|
+
case "$*" in *STALL*) sleep 60; exit 0 ;; esac
|
|
81
|
+
if [ "${FAKE_JUNIE_LINES:-0}" -gt 0 ]; then
|
|
82
|
+
: > "$wt/oversized.txt"
|
|
83
|
+
for ((line = 0; line < FAKE_JUNIE_LINES; line++)); do
|
|
84
|
+
printf 'line %s\n' "$line" >> "$wt/oversized.txt"
|
|
85
|
+
done
|
|
86
|
+
else
|
|
87
|
+
printf 'solution %s\n' "$*" > "$wt/solution.txt"
|
|
88
|
+
fi
|
|
89
|
+
if [ "${FAKE_JUNIE_TRY_COMMIT:-0}" = "1" ]; then
|
|
90
|
+
git -C "$wt" add solution.txt
|
|
91
|
+
commit_rc=0
|
|
92
|
+
git -C "$wt" commit --no-verify -m "agent-owned-commit" \
|
|
93
|
+
> "$wt/../../runs/junie-commit-attempt.log" 2>&1 || commit_rc=$?
|
|
94
|
+
printf '%s\n' "$commit_rc" > "$wt/../../runs/junie-commit-attempt.rc"
|
|
95
|
+
fi
|
|
96
|
+
sleep 2
|
|
97
|
+
case "$*" in
|
|
98
|
+
*EXIT_3*) exit 3 ;;
|
|
99
|
+
*EXIT_5*) exit 5 ;;
|
|
100
|
+
esac
|
|
101
|
+
exit 0
|
|
102
|
+
FAKE
|
|
103
|
+
cp bin/junie bin/kilo
|
|
104
|
+
chmod +x bin/junie bin/kilo
|
|
105
|
+
# fake codex: takes the worktree from `-C <dir>`, writes solution.txt, exits 0/3/5.
|
|
106
|
+
cat > bin/codex <<'FAKE'
|
|
107
|
+
#!/usr/bin/env bash
|
|
108
|
+
set -u
|
|
109
|
+
wt=""
|
|
110
|
+
while [ $# -gt 0 ]; do
|
|
111
|
+
case "$1" in
|
|
112
|
+
-C) wt="$2"; shift 2 ;;
|
|
113
|
+
*) shift ;;
|
|
114
|
+
esac
|
|
115
|
+
done
|
|
116
|
+
[ -n "$wt" ] || exit 9
|
|
117
|
+
case "$*" in *STALL*) sleep 60; exit 0 ;; esac
|
|
118
|
+
printf 'solution %s\n' "$*" > "$wt/solution.txt"
|
|
119
|
+
sleep 1
|
|
120
|
+
case "$*" in
|
|
121
|
+
*EXIT_3*) exit 3 ;;
|
|
122
|
+
*EXIT_5*) exit 5 ;;
|
|
123
|
+
esac
|
|
124
|
+
exit 0
|
|
125
|
+
FAKE
|
|
126
|
+
chmod +x bin/codex
|
|
127
|
+
echo "initial" > solution.txt
|
|
128
|
+
git add -A
|
|
129
|
+
git commit -q -m "initial"
|
|
130
|
+
git rev-parse HEAD > "tmp/meta/$name.base"
|
|
131
|
+
export PATH="$TEST_REPO/bin:$PATH"
|
|
132
|
+
export TASK_REPORT_LOG="$TEST_REPO/tmp/runs/task-report.log"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
cleanup_repo() {
|
|
136
|
+
rm -rf "$TEST_REPO"
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
# run_runner <name> <junie-task.sh|kilo-task.sh> <model> <verify-cmd> <task-text>
|
|
140
|
+
# Runs the runner; leaves its exit code in $RUNNER_RC.
|
|
141
|
+
run_runner() {
|
|
142
|
+
local name="$1" script="$2" model="$3" verify="$4" text="$5"
|
|
143
|
+
local rc=0
|
|
144
|
+
if [ "$script" = "junie-task.sh" ]; then
|
|
145
|
+
JUNIE_MODEL="$model" JUNIE_VERIFY_CMD="$verify" JUNIE_PING=1 JUNIE_TIMEOUT=120 \
|
|
146
|
+
bash scripts/junie-task.sh "$name" "$text" >"tmp/runs/$name.runner.out" 2>&1 || rc=$?
|
|
147
|
+
else
|
|
148
|
+
KILO_MODEL="$model" KILO_VERIFY_CMD="$verify" KILO_PING=1 KILO_TIMEOUT=120 \
|
|
149
|
+
bash scripts/kilo-task.sh "$name" "$text" >"tmp/runs/$name.runner.out" 2>&1 || rc=$?
|
|
150
|
+
fi
|
|
151
|
+
RUNNER_RC=$rc
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# json_field <file> <key>: print one field of a JSON summary ("" when absent).
|
|
155
|
+
json_field() {
|
|
156
|
+
python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get(sys.argv[2], ""))' "$1" "$2"
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
# status_lines_ok <name> <runner>: lines of tmp/runs/<name>.status that do NOT end
|
|
160
|
+
# with the model=/runner= tail (empty when every line carries the tail).
|
|
161
|
+
status_lines_ok() {
|
|
162
|
+
grep -v "model=[^ ]* runner=$2\$" "tmp/runs/$1.status" 2>/dev/null || true
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
# ── test cases ───────────────────────────────────────────────────────────
|
|
166
|
+
|
|
167
|
+
test_success_junie() {
|
|
168
|
+
make_repo ok
|
|
169
|
+
trap cleanup_repo RETURN
|
|
170
|
+
|
|
171
|
+
run_runner ok junie-task.sh junie-model true "write the solution EXIT_0"
|
|
172
|
+
|
|
173
|
+
if [ "$RUNNER_RC" -eq 0 ]; then
|
|
174
|
+
pass "success (junie): runner exit 0"
|
|
175
|
+
else
|
|
176
|
+
fail "success (junie): runner exit" "runner exit $RUNNER_RC, want 0"
|
|
177
|
+
return
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
local bad
|
|
181
|
+
bad="$(status_lines_ok ok junie)"
|
|
182
|
+
if [ -z "$bad" ]; then
|
|
183
|
+
pass "success (junie): all status lines carry model=/runner=junie"
|
|
184
|
+
else
|
|
185
|
+
fail "success (junie): status lines" "lines without the model/runner tail: $bad"
|
|
186
|
+
return
|
|
187
|
+
fi
|
|
188
|
+
|
|
189
|
+
if python3 -c 'import json,sys; json.load(open(sys.argv[1]))' "tmp/runs/ok.summary.json" 2>/dev/null; then
|
|
190
|
+
pass "success (junie): summary.json is valid JSON"
|
|
191
|
+
else
|
|
192
|
+
fail "success (junie): summary.json" "not valid JSON"
|
|
193
|
+
return
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
local runner model verdict agent_exit verify_exit files_changed agent
|
|
197
|
+
runner="$(json_field tmp/runs/ok.summary.json runner)"
|
|
198
|
+
model="$(json_field tmp/runs/ok.summary.json model)"
|
|
199
|
+
verdict="$(json_field tmp/runs/ok.summary.json verdict)"
|
|
200
|
+
agent_exit="$(json_field tmp/runs/ok.summary.json agent_exit)"
|
|
201
|
+
verify_exit="$(json_field tmp/runs/ok.summary.json verify_exit)"
|
|
202
|
+
files_changed="$(json_field tmp/runs/ok.summary.json files_changed)"
|
|
203
|
+
agent="$(json_field tmp/runs/ok.summary.json agent)"
|
|
204
|
+
if [ "$runner" = "junie" ] && [ "$model" = "junie-model" ] && [ "$verdict" = "verified" ] \
|
|
205
|
+
&& [ "$agent_exit" = "0" ] && [ "$verify_exit" = "0" ] && [ "$files_changed" = "1" ] \
|
|
206
|
+
&& [ -z "$agent" ]; then
|
|
207
|
+
pass "success (junie): summary fields"
|
|
208
|
+
else
|
|
209
|
+
fail "success (junie): summary fields" "runner=$runner model=$model verdict=$verdict agent_exit=$agent_exit verify_exit=$verify_exit files_changed=$files_changed agent='$agent'"
|
|
210
|
+
fi
|
|
211
|
+
|
|
212
|
+
local out
|
|
213
|
+
out="$(bash scripts/junie-status.sh ok)"
|
|
214
|
+
if printf '%s\n' "$out" | grep -q '^runner: junie$' && printf '%s\n' "$out" | grep -q '^model: junie-model$'; then
|
|
215
|
+
pass "success (junie): junie-status.sh prints runner/model"
|
|
216
|
+
else
|
|
217
|
+
fail "success (junie): junie-status.sh" "output missing runner/model: $out"
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
if grep -q 'model=junie-model' "tmp/runs/task-report.log" 2>/dev/null; then
|
|
221
|
+
pass "success (junie): tracker note includes model="
|
|
222
|
+
else
|
|
223
|
+
fail "success (junie): tracker note" "note log lacks model=: $(cat tmp/runs/task-report.log 2>/dev/null || true)"
|
|
224
|
+
fi
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
test_junie_commit_is_blocked_but_parent_runner_commits() {
|
|
228
|
+
make_repo guarded
|
|
229
|
+
trap cleanup_repo RETURN
|
|
230
|
+
|
|
231
|
+
local rc=0 attempt_rc attempt_log subject body commit_count
|
|
232
|
+
FAKE_JUNIE_TRY_COMMIT=1 JUNIE_MODEL=guard-model JUNIE_VERIFY_CMD=true JUNIE_PING=1 JUNIE_TIMEOUT=120 \
|
|
233
|
+
bash scripts/junie-task.sh guarded "write the solution" >"tmp/runs/guarded.runner.out" 2>&1 || rc=$?
|
|
234
|
+
attempt_rc="$(cat tmp/runs/junie-commit-attempt.rc 2>/dev/null || true)"
|
|
235
|
+
attempt_log="$(cat tmp/runs/junie-commit-attempt.log 2>/dev/null || true)"
|
|
236
|
+
subject="$(git -C tmp/worktrees/guarded log -1 --format=%s 2>/dev/null || true)"
|
|
237
|
+
body="$(git -C tmp/worktrees/guarded log -1 --format=%B 2>/dev/null || true)"
|
|
238
|
+
commit_count="$(git rev-list --count HEAD)"
|
|
239
|
+
if [ "$rc" -eq 0 ] && [ "$attempt_rc" -ne 0 ] \
|
|
240
|
+
&& printf '%s\n' "$attempt_log" | grep -q 'Task coder agents may not commit' \
|
|
241
|
+
&& [ "$subject" != "agent-owned-commit" ] && [ "$commit_count" -eq 2 ] \
|
|
242
|
+
&& printf '%s\n' "$body" | grep -q '^runner: junie$' \
|
|
243
|
+
&& printf '%s\n' "$body" | grep -q '^model: guard-model$'; then
|
|
244
|
+
pass "Junie --no-verify commit is blocked; parent runner commits with trailers"
|
|
245
|
+
else
|
|
246
|
+
fail "Junie commit guard" "rc=$rc attempt=$attempt_rc subject='$subject' count=$commit_count"
|
|
247
|
+
fi
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
test_kilo_commit_is_blocked_but_parent_runner_commits() {
|
|
251
|
+
make_repo guarded-kilo
|
|
252
|
+
trap cleanup_repo RETURN
|
|
253
|
+
|
|
254
|
+
local rc=0 attempt_rc attempt_log subject body commit_count
|
|
255
|
+
FAKE_JUNIE_TRY_COMMIT=1 KILO_MODEL=guard-model KILO_VERIFY_CMD=true KILO_PING=1 KILO_TIMEOUT=120 \
|
|
256
|
+
bash scripts/kilo-task.sh guarded-kilo "write the solution" >"tmp/runs/guarded-kilo.runner.out" 2>&1 || rc=$?
|
|
257
|
+
attempt_rc="$(cat tmp/runs/junie-commit-attempt.rc 2>/dev/null || true)"
|
|
258
|
+
attempt_log="$(cat tmp/runs/junie-commit-attempt.log 2>/dev/null || true)"
|
|
259
|
+
subject="$(git -C tmp/worktrees/guarded-kilo log -1 --format=%s 2>/dev/null || true)"
|
|
260
|
+
body="$(git -C tmp/worktrees/guarded-kilo log -1 --format=%B 2>/dev/null || true)"
|
|
261
|
+
commit_count="$(git rev-list --count HEAD)"
|
|
262
|
+
if [ "$rc" -eq 0 ] && [ "$attempt_rc" -ne 0 ] \
|
|
263
|
+
&& printf '%s\n' "$attempt_log" | grep -q 'Task coder agents may not commit' \
|
|
264
|
+
&& [ "$subject" != "agent-owned-commit" ] && [ "$commit_count" -eq 2 ] \
|
|
265
|
+
&& printf '%s\n' "$body" | grep -q '^runner: kilo$' \
|
|
266
|
+
&& printf '%s\n' "$body" | grep -q '^model: guard-model$'; then
|
|
267
|
+
pass "Kilo --no-verify commit is blocked; parent runner commits with trailers"
|
|
268
|
+
else
|
|
269
|
+
fail "Kilo commit guard" "rc=$rc attempt=$attempt_rc subject='$subject' count=$commit_count"
|
|
270
|
+
fi
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
test_kilo_uses_configured_model_when_unset() {
|
|
274
|
+
make_repo configured-model
|
|
275
|
+
trap cleanup_repo RETURN
|
|
276
|
+
|
|
277
|
+
local rc=0 reported_model
|
|
278
|
+
FAKE_KILO_ARGS_LOG="$TEST_REPO/tmp/runs/kilo.args" KILO_VERIFY_CMD=true KILO_PING=1 KILO_TIMEOUT=120 \
|
|
279
|
+
bash scripts/kilo-task.sh configured-model "write the solution" >"tmp/runs/configured-model.runner.out" 2>&1 || rc=$?
|
|
280
|
+
reported_model="$(json_field tmp/runs/configured-model.summary.json model)"
|
|
281
|
+
if [ "$rc" -eq 0 ] && [ "$reported_model" = "configured-default" ] \
|
|
282
|
+
&& ! grep -Eq '(^| )-m( |$)|--model' tmp/runs/kilo.args; then
|
|
283
|
+
pass "Kilo leaves model selection to its configuration when KILO_MODEL is unset"
|
|
284
|
+
else
|
|
285
|
+
fail "Kilo configured model" "rc=$rc model=$reported_model args=$(cat tmp/runs/kilo.args 2>/dev/null)"
|
|
286
|
+
fi
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
test_kilo_oversized_diff_is_preserved_without_commit() {
|
|
290
|
+
make_repo oversized-kilo
|
|
291
|
+
trap cleanup_repo RETURN
|
|
292
|
+
|
|
293
|
+
local rc=0 base head commit_log patch_lines
|
|
294
|
+
FAKE_JUNIE_LINES=301 KILO_MODEL=large-model KILO_VERIFY_CMD=true KILO_PING=1 KILO_TIMEOUT=120 \
|
|
295
|
+
bash scripts/kilo-task.sh oversized-kilo "write an oversized solution" >"tmp/runs/oversized-kilo.runner.out" 2>&1 || rc=$?
|
|
296
|
+
base="$(cat tmp/meta/oversized-kilo.base)"
|
|
297
|
+
head="$(git rev-parse HEAD)"
|
|
298
|
+
commit_log="$(cat tmp/runs/oversized-kilo.commit.log 2>/dev/null || true)"
|
|
299
|
+
patch_lines="$(wc -l < tmp/patches/oversized-kilo.patch 2>/dev/null || printf '0')"
|
|
300
|
+
if [ "$rc" -eq 1 ] && [ "$head" = "$base" ] \
|
|
301
|
+
&& printf '%s\n' "$commit_log" | grep -q '301 added/deleted lines (limit 300)' \
|
|
302
|
+
&& [ -f tmp/worktrees/oversized-kilo/oversized.txt ] \
|
|
303
|
+
&& [ "$(wc -l < tmp/worktrees/oversized-kilo/oversized.txt)" -eq 301 ] \
|
|
304
|
+
&& [ "$patch_lines" -gt 0 ]; then
|
|
305
|
+
pass "Kilo diff over 300 lines is not committed and worktree/patch are preserved"
|
|
306
|
+
else
|
|
307
|
+
fail "Kilo 300-line guard" "rc=$rc head=$head base=$base log='$commit_log' patch_lines=$patch_lines"
|
|
308
|
+
fi
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
test_junie_run_policy_is_explicit() {
|
|
312
|
+
if grep -qi 'Never perform Git writes' "$PROJECT_ROOT/agents/junie/RUN.md" \
|
|
313
|
+
&& grep -qi '300 added plus deleted' "$PROJECT_ROOT/agents/junie/RUN.md" \
|
|
314
|
+
&& grep -qi 'never run Git write commands' "$PROJECT_ROOT/agents/junie/INSTRUCTIONS.md" \
|
|
315
|
+
&& grep -qi 'Never perform Git writes' "$PROJECT_ROOT/agents/kilo/RUN.md" \
|
|
316
|
+
&& grep -qi '300 added plus deleted' "$PROJECT_ROOT/agents/kilo/RUN.md" \
|
|
317
|
+
&& grep -qi 'never run Git write commands' "$PROJECT_ROOT/agents/kilo/INSTRUCTIONS.md"; then
|
|
318
|
+
pass "Junie and Kilo instructions forbid Git writes and require pre-edit scope limits"
|
|
319
|
+
else
|
|
320
|
+
fail "Junie run policy" "missing no-write or 300-line instruction"
|
|
321
|
+
fi
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
test_success_codex() {
|
|
325
|
+
make_repo "cx"
|
|
326
|
+
trap cleanup_repo RETURN
|
|
327
|
+
printf 'write the solution EXIT_0\n' > spec.md
|
|
328
|
+
|
|
329
|
+
local rc=0
|
|
330
|
+
CODEX_VERIFY_CMD=true CODEX_PING=1 CODEX_TIMEOUT=120 \
|
|
331
|
+
bash scripts/codex-task.sh cx --file spec.md >/dev/null 2>&1 || rc=$?
|
|
332
|
+
local verdict runner model
|
|
333
|
+
verdict="$(json_field tmp/runs/cx.summary.json verdict)"
|
|
334
|
+
runner="$(json_field tmp/runs/cx.summary.json runner)"
|
|
335
|
+
model="$(json_field tmp/runs/cx.summary.json model)"
|
|
336
|
+
if [ "$rc" -eq 0 ] && [ "$verdict" = "verified" ] && [ "$runner" = "codex" ] && [ "$model" = "codex" ]; then
|
|
337
|
+
pass "codex runner: exit 0, verified, runner/model recorded"
|
|
338
|
+
else
|
|
339
|
+
fail "codex runner" "rc=$rc verdict=$verdict runner=$runner model=$model"
|
|
340
|
+
fi
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
test_agent_failure_kilo() {
|
|
344
|
+
make_repo agent-fail
|
|
345
|
+
trap cleanup_repo RETURN
|
|
346
|
+
|
|
347
|
+
run_runner agent-fail kilo-task.sh kilo-model true "make it fail EXIT_3"
|
|
348
|
+
|
|
349
|
+
if [ "$RUNNER_RC" -eq 3 ]; then
|
|
350
|
+
pass "agent failure (kilo): runner exit 3"
|
|
351
|
+
else
|
|
352
|
+
fail "agent failure (kilo): runner exit" "runner exit $RUNNER_RC, want 3"
|
|
353
|
+
return
|
|
354
|
+
fi
|
|
355
|
+
|
|
356
|
+
local bad
|
|
357
|
+
bad="$(status_lines_ok agent-fail kilo)"
|
|
358
|
+
if [ -z "$bad" ]; then
|
|
359
|
+
pass "agent failure (kilo): all status lines carry model=/runner=kilo"
|
|
360
|
+
else
|
|
361
|
+
fail "agent failure (kilo): status lines" "lines without the model/runner tail: $bad"
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
local runner model verdict agent_exit verify_exit agent
|
|
365
|
+
runner="$(json_field tmp/runs/agent-fail.summary.json runner)"
|
|
366
|
+
model="$(json_field tmp/runs/agent-fail.summary.json model)"
|
|
367
|
+
verdict="$(json_field tmp/runs/agent-fail.summary.json verdict)"
|
|
368
|
+
agent_exit="$(json_field tmp/runs/agent-fail.summary.json agent_exit)"
|
|
369
|
+
verify_exit="$(json_field tmp/runs/agent-fail.summary.json verify_exit)"
|
|
370
|
+
agent="$(json_field tmp/runs/agent-fail.summary.json agent)"
|
|
371
|
+
if [ "$runner" = "kilo" ] && [ "$model" = "kilo-model" ] && [ "$verdict" = "agent_failed" ] \
|
|
372
|
+
&& [ "$agent_exit" = "3" ] && [ "$verify_exit" = "0" ] && [ "$agent" = "code" ]; then
|
|
373
|
+
pass "agent failure (kilo): summary fields (incl. agent)"
|
|
374
|
+
else
|
|
375
|
+
fail "agent failure (kilo): summary fields" "runner=$runner model=$model verdict=$verdict agent_exit=$agent_exit verify_exit=$verify_exit agent='$agent'"
|
|
376
|
+
fi
|
|
377
|
+
|
|
378
|
+
if grep -q 'model=kilo-model' "tmp/runs/task-report.log" 2>/dev/null; then
|
|
379
|
+
pass "agent failure (kilo): tracker note includes model="
|
|
380
|
+
else
|
|
381
|
+
fail "agent failure (kilo): tracker note" "note log lacks model=: $(cat tmp/runs/task-report.log 2>/dev/null || true)"
|
|
382
|
+
fi
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
test_verify_failure_junie() {
|
|
386
|
+
make_repo verify-fail
|
|
387
|
+
trap cleanup_repo RETURN
|
|
388
|
+
|
|
389
|
+
run_runner verify-fail junie-task.sh junie-model false "write the solution EXIT_0"
|
|
390
|
+
|
|
391
|
+
if [ "$RUNNER_RC" -eq 1 ]; then
|
|
392
|
+
pass "verification failure (junie): runner exit 1"
|
|
393
|
+
else
|
|
394
|
+
fail "verification failure (junie): runner exit" "runner exit $RUNNER_RC, want 1"
|
|
395
|
+
return
|
|
396
|
+
fi
|
|
397
|
+
|
|
398
|
+
local verdict agent_exit verify_exit
|
|
399
|
+
verdict="$(json_field tmp/runs/verify-fail.summary.json verdict)"
|
|
400
|
+
agent_exit="$(json_field tmp/runs/verify-fail.summary.json agent_exit)"
|
|
401
|
+
verify_exit="$(json_field tmp/runs/verify-fail.summary.json verify_exit)"
|
|
402
|
+
if [ "$verdict" = "verify_failed" ] && [ "$agent_exit" = "0" ] && [ "$verify_exit" = "1" ]; then
|
|
403
|
+
pass "verification failure (junie): summary fields"
|
|
404
|
+
else
|
|
405
|
+
fail "verification failure (junie): summary fields" "verdict=$verdict agent_exit=$agent_exit verify_exit=$verify_exit"
|
|
406
|
+
fi
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
test_status_fallback() {
|
|
410
|
+
# No summary yet: junie-status.sh takes runner/model from the last status line.
|
|
411
|
+
make_repo fb
|
|
412
|
+
trap cleanup_repo RETURN
|
|
413
|
+
|
|
414
|
+
echo "[00:00] junie running - 1 file(s) changed; last: working model=junie-lite runner=junie" > "tmp/runs/fb.status"
|
|
415
|
+
touch "tmp/runs/fb.stdout.log"
|
|
416
|
+
local out
|
|
417
|
+
out="$(bash scripts/junie-status.sh fb)"
|
|
418
|
+
if printf '%s\n' "$out" | grep -q '^runner: junie$' && printf '%s\n' "$out" | grep -q '^model: junie-lite$'; then
|
|
419
|
+
pass "junie-status.sh: fallback to last status line"
|
|
420
|
+
else
|
|
421
|
+
fail "junie-status.sh: fallback" "output: $out"
|
|
422
|
+
fi
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
test_prechange_run() {
|
|
426
|
+
# Runs that predate model recording have no summary and no model= in the status
|
|
427
|
+
# file; junie-status.sh must still work and print no runner/model lines.
|
|
428
|
+
make_repo pre
|
|
429
|
+
trap cleanup_repo RETURN
|
|
430
|
+
|
|
431
|
+
echo "[00:00] starting - worktree=/x" > "tmp/runs/pre.status"
|
|
432
|
+
touch "tmp/runs/pre.stdout.log"
|
|
433
|
+
local rc=0 out
|
|
434
|
+
out="$(bash scripts/junie-status.sh pre)" || rc=$?
|
|
435
|
+
if [ "$rc" -eq 0 ] && ! printf '%s\n' "$out" | grep -q '^runner:'; then
|
|
436
|
+
pass "junie-status.sh: pre-change run still works"
|
|
437
|
+
else
|
|
438
|
+
fail "junie-status.sh: pre-change run" "rc=$rc output: $out"
|
|
439
|
+
fi
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
# The runner closes the tracker task itself after the commit and verification:
|
|
443
|
+
# green -> done, red -> blocked (with the failing check as reason).
|
|
444
|
+
test_close_status_after_commit() {
|
|
445
|
+
make_repo "close"
|
|
446
|
+
trap cleanup_repo RETURN
|
|
447
|
+
printf 'write the solution EXIT_0\n' > spec.md
|
|
448
|
+
|
|
449
|
+
JUNIE_MODEL=jm JUNIE_VERIFY_CMD=true JUNIE_PING=1 JUNIE_TIMEOUT=120 \
|
|
450
|
+
bash scripts/junie-task.sh close --file spec.md --task-id 7.9 >/dev/null 2>&1 || true
|
|
451
|
+
if grep -q '^close 7.9 green' tmp/runs/task-report.log 2>/dev/null; then
|
|
452
|
+
pass "runner closes the task green after a verified commit"
|
|
453
|
+
else
|
|
454
|
+
fail "runner close green" "log: $(cat tmp/runs/task-report.log 2>/dev/null)"
|
|
455
|
+
fi
|
|
456
|
+
|
|
457
|
+
: > tmp/runs/task-report.log
|
|
458
|
+
JUNIE_MODEL=jm JUNIE_VERIFY_CMD=false JUNIE_PING=1 JUNIE_TIMEOUT=120 \
|
|
459
|
+
bash scripts/junie-task.sh close --file spec.md --task-id 7.9 >/dev/null 2>&1 || true
|
|
460
|
+
if grep -q '^close 7.9 red ' tmp/runs/task-report.log 2>/dev/null; then
|
|
461
|
+
pass "runner closes the task red (blocked) after a failed verification"
|
|
462
|
+
else
|
|
463
|
+
fail "runner close red" "log: $(cat tmp/runs/task-report.log 2>/dev/null)"
|
|
464
|
+
fi
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
# Real task-report.sh: `close green` maps to done, `close red <reason>` to blocked with the reason.
|
|
468
|
+
test_task_report_close() {
|
|
469
|
+
local root out; root="$(mktemp -d)"
|
|
470
|
+
(
|
|
471
|
+
export VIBE_TASKS_ROOT="$root"
|
|
472
|
+
python3 "$PROJECT_ROOT/scripts/tasks_init.py" --path "$root/tasks/tasks.db" --project T >/dev/null 2>&1
|
|
473
|
+
python3 "$PROJECT_ROOT/scripts/tasks.py" add-sprint S >/dev/null 2>&1
|
|
474
|
+
python3 "$PROJECT_ROOT/scripts/tasks.py" add 1 T1 >/dev/null 2>&1
|
|
475
|
+
bash "$PROJECT_ROOT/scripts/task-report.sh" close 1.1 green >/dev/null 2>&1
|
|
476
|
+
python3 "$PROJECT_ROOT/scripts/tasks.py" show 1.1 > "$root/green.json" 2>/dev/null
|
|
477
|
+
python3 "$PROJECT_ROOT/scripts/tasks.py" reset 1.1 >/dev/null 2>&1
|
|
478
|
+
bash "$PROJECT_ROOT/scripts/task-report.sh" close 1.1 red "bad check" >/dev/null 2>&1
|
|
479
|
+
python3 "$PROJECT_ROOT/scripts/tasks.py" show 1.1 > "$root/red.json" 2>/dev/null
|
|
480
|
+
)
|
|
481
|
+
out="$(python3 -c 'import json,sys; d=sys.argv[1]; g=json.load(open(d+"/green.json")); r=json.load(open(d+"/red.json")); print(g["effective_status"], r["effective_status"], r["reason"])' "$root")"
|
|
482
|
+
if [ "$out" = "done blocked bad check" ]; then
|
|
483
|
+
pass "task-report close: green -> done, red -> blocked with reason"
|
|
484
|
+
else
|
|
485
|
+
fail "task-report close mapping" "got: '$out'"
|
|
486
|
+
fi
|
|
487
|
+
rm -rf "$root"
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
# ── main ─────────────────────────────────────────────────────────────────
|
|
491
|
+
|
|
492
|
+
echo "=== runner model recording tests ==="
|
|
493
|
+
|
|
494
|
+
# These tests use minimal spec files that do not follow the spec template;
|
|
495
|
+
# disable spec-lint so the existing assertions remain valid.
|
|
496
|
+
export SPEC_LINT=off
|
|
497
|
+
|
|
498
|
+
# A stuck agent (no file changed after RUNNER_STALL seconds) is killed and reported.
|
|
499
|
+
test_stall_detector() {
|
|
500
|
+
local script
|
|
501
|
+
for script in junie-task.sh kilo-task.sh; do
|
|
502
|
+
make_repo "stall"
|
|
503
|
+
local start=$SECONDS rc=0
|
|
504
|
+
if [ "$script" = "junie-task.sh" ]; then
|
|
505
|
+
RUNNER_STALL=2 JUNIE_PING=1 JUNIE_TIMEOUT=120 JUNIE_VERIFY_CMD=true \
|
|
506
|
+
bash scripts/junie-task.sh stall "do nothing STALL" >tmp/runs/stall.runner.out 2>&1 || rc=$?
|
|
507
|
+
else
|
|
508
|
+
RUNNER_STALL=2 KILO_PING=1 KILO_TIMEOUT=120 KILO_VERIFY_CMD=true \
|
|
509
|
+
bash scripts/kilo-task.sh stall "do nothing STALL" >tmp/runs/stall.runner.out 2>&1 || rc=$?
|
|
510
|
+
fi
|
|
511
|
+
local took=$((SECONDS - start))
|
|
512
|
+
if [ "$rc" -eq 3 ] && grep -q "stalled" tmp/runs/stall.status tmp/runs/stall.runner.out && [ "$took" -lt 40 ]; then
|
|
513
|
+
pass "stall detector ($script): killed, exit 3, 'stalled' reported in ${took}s"
|
|
514
|
+
else
|
|
515
|
+
fail "stall detector ($script)" "rc=$rc took=${took}s status=$(tail -1 tmp/runs/stall.status 2>/dev/null)"
|
|
516
|
+
fi
|
|
517
|
+
cleanup_repo
|
|
518
|
+
done
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
# run-task.sh: junie fails (exit 3) -> exactly one retry with kilo, which succeeds; a
|
|
522
|
+
# verification failure is NOT retried; fallback=none keeps exit 3.
|
|
523
|
+
test_run_task_fallback() {
|
|
524
|
+
make_repo "fb"
|
|
525
|
+
sed '/EXIT_3/d;/EXIT_5/d' bin/junie > bin/kilo; chmod +x bin/kilo
|
|
526
|
+
printf 'make it fail EXIT_3\n' > spec.md
|
|
527
|
+
local rc=0
|
|
528
|
+
JUNIE_MODEL=jm KILO_MODEL=km JUNIE_VERIFY_CMD=true KILO_VERIFY_CMD=true JUNIE_PING=1 KILO_PING=1 \
|
|
529
|
+
bash scripts/run-task.sh fb --file spec.md --runner junie --fallback kilo >tmp/runs/fb.rt.out 2>&1 || rc=$?
|
|
530
|
+
local attempts; attempts="$(cat tmp/runs/fb.attempts 2>/dev/null)"
|
|
531
|
+
if [ "$rc" -eq 0 ] && [ "$(printf '%s\n' "$attempts" | wc -l)" -eq 2 ] \
|
|
532
|
+
&& printf '%s' "$attempts" | grep -q "runner=junie exit=3" && printf '%s' "$attempts" | grep -q "runner=kilo exit=0"; then
|
|
533
|
+
pass "run-task: junie failure falls back to kilo once, overall exit 0, both attempts recorded"
|
|
534
|
+
else
|
|
535
|
+
fail "run-task fallback" "rc=$rc attempts=$attempts"
|
|
536
|
+
fi
|
|
537
|
+
cleanup_repo
|
|
538
|
+
|
|
539
|
+
make_repo "default-codex"
|
|
540
|
+
printf 'write a successful solution\n' > spec.md; rc=0
|
|
541
|
+
CODEX_VERIFY_CMD=true CODEX_PING=1 \
|
|
542
|
+
bash scripts/run-task.sh default-codex --file spec.md >tmp/runs/default-codex.rt.out 2>&1 || rc=$?
|
|
543
|
+
local default_attempts default_runner
|
|
544
|
+
default_attempts="$(cat tmp/runs/default-codex.attempts 2>/dev/null)"
|
|
545
|
+
default_runner="$(json_field tmp/runs/default-codex.summary.json runner)"
|
|
546
|
+
if [ "$rc" -eq 0 ] && [ "$(printf '%s\n' "$default_attempts" | wc -l)" -eq 1 ] \
|
|
547
|
+
&& printf '%s' "$default_attempts" | grep -q 'runner=codex exit=0' \
|
|
548
|
+
&& [ "$default_runner" = "codex" ]; then
|
|
549
|
+
pass "run-task: default coder is Codex, single attempt, no fallback"
|
|
550
|
+
else
|
|
551
|
+
fail "run-task default Codex" "rc=$rc runner=$default_runner attempts=$default_attempts"
|
|
552
|
+
fi
|
|
553
|
+
cleanup_repo
|
|
554
|
+
|
|
555
|
+
make_repo "vf"
|
|
556
|
+
printf 'write it EXIT_0\n' > spec.md; rc=0
|
|
557
|
+
JUNIE_MODEL=jm JUNIE_VERIFY_CMD=false JUNIE_PING=1 bash scripts/run-task.sh vf --file spec.md >/dev/null 2>&1 || rc=$?
|
|
558
|
+
if [ "$rc" -eq 1 ] && [ "$(wc -l < tmp/runs/vf.attempts)" -eq 1 ]; then
|
|
559
|
+
pass "run-task: verification failure (exit 1) is not retried on another runner"
|
|
560
|
+
else
|
|
561
|
+
fail "run-task no retry on verify failure" "rc=$rc"
|
|
562
|
+
fi
|
|
563
|
+
cleanup_repo
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
test_success_junie
|
|
567
|
+
test_junie_commit_is_blocked_but_parent_runner_commits
|
|
568
|
+
test_kilo_commit_is_blocked_but_parent_runner_commits
|
|
569
|
+
test_kilo_uses_configured_model_when_unset
|
|
570
|
+
test_kilo_oversized_diff_is_preserved_without_commit
|
|
571
|
+
test_junie_run_policy_is_explicit
|
|
572
|
+
test_success_codex
|
|
573
|
+
test_agent_failure_kilo
|
|
574
|
+
test_verify_failure_junie
|
|
575
|
+
test_status_fallback
|
|
576
|
+
test_prechange_run
|
|
577
|
+
test_stall_detector
|
|
578
|
+
test_run_task_fallback
|
|
579
|
+
test_close_status_after_commit
|
|
580
|
+
test_task_report_close
|
|
581
|
+
|
|
582
|
+
echo ""
|
|
583
|
+
echo "=== Results: $passed/$total passed, $failed failed ==="
|
|
584
|
+
|
|
585
|
+
if [ "$failed" -gt 0 ]; then
|
|
586
|
+
exit 1
|
|
587
|
+
fi
|
|
588
|
+
exit 0
|