@walwal-harness/cli 7.1.54 → 7.1.56
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.
- package/CHANGELOG.md +18 -1
- package/HR-Resource/cdo/SKILL.md +30 -7
- package/HR-Resource/ceo/SKILL.md +20 -0
- package/HR-Resource/coo/SKILL.md +30 -7
- package/HR-Resource/cqo/SKILL.md +41 -7
- package/HR-Resource/cto/SKILL.md +30 -7
- package/HR-Resource/hiring/SKILL.md +14 -1
- package/HR-Resource/ops/SKILL.md +39 -8
- package/apps/harness-dashboard/package-lock.json +5746 -0
- package/assets/templates/AGENTS.md.template +4 -1
- package/assets/templates/config.json +5 -1
- package/assets/templates/worker-report.md.template +16 -0
- package/bin/init.js +98 -1
- package/commands/goal.md +2 -0
- package/commands/hot-fix.md +4 -0
- package/commands/submission.md +2 -0
- package/conventions/cqo.md +2 -0
- package/conventions/ops.md +1 -0
- package/conventions/shared.md +15 -0
- package/gotchas/cqo.md +12 -0
- package/gotchas/cto.md +4 -0
- package/gotchas/ops.md +4 -0
- package/gotchas/shared.md +18 -0
- package/package.json +1 -1
- package/scripts/harness-company-complete.sh +24 -0
- package/scripts/harness-hourly-review.sh +1 -1
- package/scripts/harness-lessons-gate.sh +130 -0
- package/scripts/harness-stop.sh +25 -1
- package/scripts/harness-user-prompt-submit.sh +1 -1
- package/scripts/harness-wake.sh +14 -7
- package/scripts/harness-worker-dispatch.sh +33 -11
- package/scripts/harness-worker-evidence-validate.sh +12 -5
package/scripts/harness-wake.sh
CHANGED
|
@@ -130,7 +130,7 @@ start_headless_tick() {
|
|
|
130
130
|
local idle="$2"
|
|
131
131
|
local force="$3"
|
|
132
132
|
local review_path="$4"
|
|
133
|
-
local mode executor wake_model agent_bin stamp ops_dir activity_dir prompt_scratch log_rel log_path pid_file old_pid pid status tmux_session session_name session_safe wake_prompt log_note
|
|
133
|
+
local mode executor wake_model agent_bin auto_approve_flag stamp ops_dir activity_dir prompt_scratch log_rel log_path pid_file old_pid pid status tmux_session session_name session_safe wake_prompt log_note
|
|
134
134
|
|
|
135
135
|
if command -v jq >/dev/null 2>&1 && [ -f "$project_root/.harness/config.json" ]; then
|
|
136
136
|
mode="${HARNESS_WAKE_MODE:-$(jq -r '.company_mode.hourly_wake_mode // "headless"' "$project_root/.harness/config.json" 2>/dev/null || echo headless)}"
|
|
@@ -141,6 +141,13 @@ start_headless_tick() {
|
|
|
141
141
|
executor="${HARNESS_WAKE_EXECUTOR:-claude}"
|
|
142
142
|
wake_model="${HARNESS_WAKE_MODEL:-}"
|
|
143
143
|
fi
|
|
144
|
+
# Autonomous wake agents run unattended: accept all tool/MCP usage so the tick
|
|
145
|
+
# never blocks on a permission prompt nobody is there to answer (default = accept all).
|
|
146
|
+
if [ "$executor" = "codex" ]; then
|
|
147
|
+
auto_approve_flag="--dangerously-bypass-approvals-and-sandbox"
|
|
148
|
+
else
|
|
149
|
+
auto_approve_flag="--dangerously-skip-permissions"
|
|
150
|
+
fi
|
|
144
151
|
stamp="$(date -u "+%Y%m%dT%H%M%SZ")"
|
|
145
152
|
ops_dir="$project_root/.harness/ops/wake"
|
|
146
153
|
activity_dir="$project_root/.harness/activity"
|
|
@@ -237,12 +244,12 @@ EOF
|
|
|
237
244
|
(
|
|
238
245
|
cd "$project_root" || exit 1
|
|
239
246
|
if [ "$executor" = "codex" ]; then
|
|
240
|
-
"$agent_bin" exec -C "$project_root" "$wake_prompt" > "$log_path" 2>&1
|
|
247
|
+
"$agent_bin" exec $auto_approve_flag -C "$project_root" "$wake_prompt" > "$log_path" 2>&1
|
|
241
248
|
else
|
|
242
249
|
if [ -n "$wake_model" ]; then
|
|
243
|
-
"$agent_bin" -p "$wake_prompt" --model "$wake_model" > "$log_path" 2>&1
|
|
250
|
+
"$agent_bin" $auto_approve_flag -p "$wake_prompt" --model "$wake_model" > "$log_path" 2>&1
|
|
244
251
|
else
|
|
245
|
-
"$agent_bin" -p "$wake_prompt" > "$log_path" 2>&1
|
|
252
|
+
"$agent_bin" $auto_approve_flag -p "$wake_prompt" > "$log_path" 2>&1
|
|
246
253
|
fi
|
|
247
254
|
fi
|
|
248
255
|
) &
|
|
@@ -262,12 +269,12 @@ EOF
|
|
|
262
269
|
session_safe="$(basename "$project_root" | tr -c '[:alnum:]_' '_')"
|
|
263
270
|
session_name="walwal_${session_safe}_${stamp}"
|
|
264
271
|
if [ "$executor" = "codex" ]; then
|
|
265
|
-
tmux new-session -d -s "$session_name" "cd '$project_root' && '$agent_bin' exec -C '$project_root' - < '$prompt_scratch' 2>&1 | tee '$log_path'"
|
|
272
|
+
tmux new-session -d -s "$session_name" "cd '$project_root' && '$agent_bin' exec $auto_approve_flag -C '$project_root' - < '$prompt_scratch' 2>&1 | tee '$log_path'"
|
|
266
273
|
else
|
|
267
274
|
if [ -n "$wake_model" ]; then
|
|
268
|
-
tmux new-session -d -s "$session_name" "cd '$project_root' && '$agent_bin' -p \"\$(cat '$prompt_scratch')\" --model '$wake_model' 2>&1 | tee '$log_path'"
|
|
275
|
+
tmux new-session -d -s "$session_name" "cd '$project_root' && '$agent_bin' $auto_approve_flag -p \"\$(cat '$prompt_scratch')\" --model '$wake_model' 2>&1 | tee '$log_path'"
|
|
269
276
|
else
|
|
270
|
-
tmux new-session -d -s "$session_name" "cd '$project_root' && '$agent_bin' -p \"\$(cat '$prompt_scratch')\" 2>&1 | tee '$log_path'"
|
|
277
|
+
tmux new-session -d -s "$session_name" "cd '$project_root' && '$agent_bin' $auto_approve_flag -p \"\$(cat '$prompt_scratch')\" 2>&1 | tee '$log_path'"
|
|
271
278
|
fi
|
|
272
279
|
fi
|
|
273
280
|
status="spawned-tmux executor=$executor session=$session_name"
|
|
@@ -62,12 +62,28 @@ acquire_dispatch_lock() {
|
|
|
62
62
|
acquire_dispatch_lock
|
|
63
63
|
|
|
64
64
|
spawn_mode="$(jq -r '.company_mode.worker_spawn // "claude"' "$CONFIG" 2>/dev/null || echo claude)"
|
|
65
|
+
|
|
66
|
+
# AGENTS.md Hard Rule 21 — every worker spawn declares its model explicitly.
|
|
67
|
+
# Never inherit the CLI default: a worker terminated by a usage limit looks
|
|
68
|
+
# exactly like a worker that finished, and without a declared model there is
|
|
69
|
+
# nothing to check the limit against. Falls back to the phase model from
|
|
70
|
+
# flow.team, then to opus.
|
|
71
|
+
resolve_worker_model() {
|
|
72
|
+
local phase="$1" model
|
|
73
|
+
model="$(jq -r --arg p "$phase" '
|
|
74
|
+
.company_mode.worker_model
|
|
75
|
+
// (if $p == "eval" then .flow.team.eval_model else .flow.team.gen_model end)
|
|
76
|
+
// empty' "$CONFIG" 2>/dev/null || true)"
|
|
77
|
+
[ -n "$model" ] && [ "$model" != "null" ] || model="opus"
|
|
78
|
+
printf '%s' "$model"
|
|
79
|
+
}
|
|
80
|
+
|
|
65
81
|
pipeline="$(jq -r '.pipeline // "FULLSTACK"' "$PROGRESS" 2>/dev/null || echo FULLSTACK)"
|
|
66
82
|
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
67
83
|
stamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
68
84
|
|
|
69
85
|
build_prompt() {
|
|
70
|
-
local team="$1" fid="$2" agent="$3" prompt_path="$4" log_path="$5"
|
|
86
|
+
local team="$1" fid="$2" agent="$3" prompt_path="$4" log_path="$5" model="${6:-}"
|
|
71
87
|
cat > "$prompt_path" <<EOF
|
|
72
88
|
You are running as walwal-harness company worker.
|
|
73
89
|
|
|
@@ -75,9 +91,10 @@ Agent: harness-${agent}
|
|
|
75
91
|
Worker slot: ${team}
|
|
76
92
|
Feature ID: ${fid}
|
|
77
93
|
Project root: ${PROJECT_ROOT}
|
|
94
|
+
Declared model: ${model:-unspecified}
|
|
78
95
|
|
|
79
96
|
Follow these rules exactly:
|
|
80
|
-
1.
|
|
97
|
+
1. Lessons before plan. FIRST — before any source edit and before any measurement — read AGENTS.md, CONVENTIONS.md if present, .harness/conventions/shared.md, .harness/conventions/${agent}.md if present, .harness/gotchas/shared.md, .harness/gotchas/${agent}.md if present, .harness/memory.md, .harness/progress.json, .harness/actions/feature-list.json, .harness/actions/api-contract.json, and the relevant harness skill for ${agent}. Then write down which convention/gotcha items apply to FEATURE_ID=${fid} and why, and only then start work.
|
|
81
98
|
2. Work only on FEATURE_ID=${fid}. Do not broaden scope.
|
|
82
99
|
3. Respect IA ownership and do not edit files outside your department.
|
|
83
100
|
4. Write a report to .harness/actions/gen-report-${fid}.md if you are a generator, or .harness/actions/evaluation-${fid}.md if you are an evaluator.
|
|
@@ -85,6 +102,8 @@ Follow these rules exactly:
|
|
|
85
102
|
6. On failure, write the reason and run: bash scripts/harness-queue-manager.sh fail ${fid} "<short reason>"
|
|
86
103
|
7. Use partial progress updates only via scripts/harness-progress-set.sh.
|
|
87
104
|
8. Append real timestamped progress to .harness/progress.log. Never write future timestamps.
|
|
105
|
+
9. End the report with a one-line tally naming which of the convention/gotcha items from step 1 actually fired. \`0 fired\` is a valid tally and must be stated, not omitted.
|
|
106
|
+
10. Write the report incrementally as the work happens — create the file with its sections present up front and fill them in. If this session is killed mid-round, the report must still be a valid partial report, never a stub.
|
|
88
107
|
|
|
89
108
|
This worker log is: ${log_path#$PROJECT_ROOT/}
|
|
90
109
|
EOF
|
|
@@ -138,7 +157,8 @@ while [ "$i" -lt "$count" ]; do
|
|
|
138
157
|
continue
|
|
139
158
|
fi
|
|
140
159
|
|
|
141
|
-
|
|
160
|
+
worker_model="$(resolve_worker_model "$phase")"
|
|
161
|
+
build_prompt "$team" "$fid" "$agent" "$prompt_path" "$log_path" "$worker_model"
|
|
142
162
|
|
|
143
163
|
pid="null"
|
|
144
164
|
tmux_session="null"
|
|
@@ -146,7 +166,7 @@ while [ "$i" -lt "$count" ]; do
|
|
|
146
166
|
if [ "$spawn_mode" = "tmux" ] && command -v tmux >/dev/null 2>&1 && command -v claude >/dev/null 2>&1; then
|
|
147
167
|
session_safe="$(basename "$PROJECT_ROOT" | tr -c '[:alnum:]_' '_')"
|
|
148
168
|
session_name="walwal_${session_safe}_w${team}_${stamp}"
|
|
149
|
-
if tmux new-session -d -s "$session_name" "cd '$PROJECT_ROOT' && claude -p \"\$(cat '$prompt_path')\" > '$log_path' 2>&1"; then
|
|
169
|
+
if tmux new-session -d -s "$session_name" "cd '$PROJECT_ROOT' && claude --dangerously-skip-permissions --model '$worker_model' -p \"\$(cat '$prompt_path')\" > '$log_path' 2>&1"; then
|
|
150
170
|
pane_pid="$(tmux list-panes -t "$session_name" -F '#{pane_pid}' 2>/dev/null | head -n1)"
|
|
151
171
|
if [ -n "$pane_pid" ]; then
|
|
152
172
|
pid="$pane_pid"
|
|
@@ -160,22 +180,23 @@ while [ "$i" -lt "$count" ]; do
|
|
|
160
180
|
# tmux requested but unavailable: fall back to background claude spawn
|
|
161
181
|
(
|
|
162
182
|
cd "$PROJECT_ROOT" || exit 1
|
|
163
|
-
claude -p "$(cat "$prompt_path")" > "$log_path" 2>&1
|
|
183
|
+
claude --dangerously-skip-permissions --model "$worker_model" -p "$(cat "$prompt_path")" > "$log_path" 2>&1
|
|
164
184
|
) &
|
|
165
185
|
pid="$!"
|
|
166
186
|
status="spawned tmux-fallback-claude"
|
|
167
187
|
elif [ "$spawn_mode" = "claude" ] && command -v claude >/dev/null 2>&1; then
|
|
168
188
|
(
|
|
169
189
|
cd "$PROJECT_ROOT" || exit 1
|
|
170
|
-
claude -p "$(cat "$prompt_path")" > "$log_path" 2>&1
|
|
190
|
+
claude --dangerously-skip-permissions --model "$worker_model" -p "$(cat "$prompt_path")" > "$log_path" 2>&1
|
|
171
191
|
) &
|
|
172
192
|
pid="$!"
|
|
173
193
|
status="spawned"
|
|
174
194
|
fi
|
|
175
195
|
|
|
176
196
|
tmpq="${QUEUE}.tmp.$$.$i"
|
|
177
|
-
jq --arg tid "$team" --arg agent "$agent" --arg phase "$phase" --arg prompt "$prompt_rel" --arg log "$log_rel" --arg status "$status" --argjson pid "$pid" --argjson tmux_session "$tmux_session" --arg ts "$ts" '
|
|
197
|
+
jq --arg tid "$team" --arg agent "$agent" --arg phase "$phase" --arg prompt "$prompt_rel" --arg log "$log_rel" --arg status "$status" --argjson pid "$pid" --argjson tmux_session "$tmux_session" --arg ts "$ts" --arg model "$worker_model" '
|
|
178
198
|
.teams[$tid].pid = $pid |
|
|
199
|
+
.teams[$tid].model = $model |
|
|
179
200
|
.teams[$tid].tmux_session = $tmux_session |
|
|
180
201
|
.teams[$tid].agent = $agent |
|
|
181
202
|
.teams[$tid].phase = $phase |
|
|
@@ -184,6 +205,7 @@ while [ "$i" -lt "$count" ]; do
|
|
|
184
205
|
.teams[$tid].spawn_status = $status |
|
|
185
206
|
.teams[$tid].started_at = $ts |
|
|
186
207
|
.queue.in_progress[.teams[$tid].feature].agent = $agent |
|
|
208
|
+
.queue.in_progress[.teams[$tid].feature].model = $model |
|
|
187
209
|
.queue.in_progress[.teams[$tid].feature].phase = $phase |
|
|
188
210
|
.queue.in_progress[.teams[$tid].feature].prompt = $prompt |
|
|
189
211
|
.queue.in_progress[.teams[$tid].feature].log = $log |
|
|
@@ -191,15 +213,15 @@ while [ "$i" -lt "$count" ]; do
|
|
|
191
213
|
.queue.in_progress[.teams[$tid].feature].tmux_session = $tmux_session
|
|
192
214
|
' "$QUEUE" > "$tmpq" && mv "$tmpq" "$QUEUE"
|
|
193
215
|
|
|
194
|
-
jq -n --argjson team "$team" --arg feature "$fid" --arg agent "$agent" --arg phase "$phase" --arg prompt "$prompt_rel" --arg log "$log_rel" --arg status "$status" --argjson pid "$pid" --argjson tmux_session "$tmux_session" \
|
|
195
|
-
'{team:$team, feature:$feature, agent:$agent, phase:$phase, prompt:$prompt, log:$log, status:$status, pid:$pid, tmux_session:$tmux_session}' >> "$dispatches_file"
|
|
196
|
-
echo "$ts | conductor | worker-dispatch | $status | T${team}/${fid} | $agent phase=$phase pid=$pid" >> "$PROJECT_ROOT/.harness/progress.log"
|
|
216
|
+
jq -n --argjson team "$team" --arg feature "$fid" --arg agent "$agent" --arg phase "$phase" --arg prompt "$prompt_rel" --arg log "$log_rel" --arg status "$status" --argjson pid "$pid" --argjson tmux_session "$tmux_session" --arg model "$worker_model" \
|
|
217
|
+
'{team:$team, feature:$feature, agent:$agent, phase:$phase, model:$model, prompt:$prompt, log:$log, status:$status, pid:$pid, tmux_session:$tmux_session}' >> "$dispatches_file"
|
|
218
|
+
echo "$ts | conductor | worker-dispatch | $status | T${team}/${fid} | $agent phase=$phase model=$worker_model pid=$pid" >> "$PROJECT_ROOT/.harness/progress.log"
|
|
197
219
|
i=$((i + 1))
|
|
198
220
|
done
|
|
199
221
|
|
|
200
222
|
dispatches="$(jq -s '.' "$dispatches_file")"
|
|
201
223
|
rm -f "$dispatches_file"
|
|
202
|
-
active_workers="$(jq '[.teams | to_entries[] | select(.value.status == "busy") | {team:(.key|tonumber), feature:.value.feature, agent:.value.agent, pid:.value.pid, tmux_session:.value.tmux_session, phase:(.value.phase // "gen"), prompt:.value.prompt, log:.value.log, spawn_status:.value.spawn_status}]' "$QUEUE")"
|
|
224
|
+
active_workers="$(jq '[.teams | to_entries[] | select(.value.status == "busy") | {team:(.key|tonumber), feature:.value.feature, agent:.value.agent, model:.value.model, pid:.value.pid, tmux_session:.value.tmux_session, phase:(.value.phase // "gen"), prompt:.value.prompt, log:.value.log, spawn_status:.value.spawn_status}]' "$QUEUE")"
|
|
203
225
|
|
|
204
226
|
jq --arg ts "$ts" --argjson dispatches "$dispatches" --argjson workers "$active_workers" '
|
|
205
227
|
.company_state.active_workers = ($workers | length) |
|
|
@@ -11,14 +11,21 @@ mode="${2:-text}"
|
|
|
11
11
|
scope="${3:-all}"
|
|
12
12
|
violations=()
|
|
13
13
|
|
|
14
|
+
# Section-scoped reading (conventions/shared.md): match `^>?\s*#{1,6}` and take
|
|
15
|
+
# every hit — blockquoted or plain, at any depth, with no content filter. A
|
|
16
|
+
# heading anchored on `^#` alone misses the same heading quoted one level in,
|
|
17
|
+
# which is how in-place retractions and continuation lines get written.
|
|
18
|
+
HEADING2='^[[:space:]]*>?[[:space:]]*##[[:space:]]+'
|
|
19
|
+
HEADING3='^[[:space:]]*>?[[:space:]]*###[[:space:]]+'
|
|
20
|
+
|
|
14
21
|
has_implementation_notes() {
|
|
15
22
|
local file="$1"
|
|
16
23
|
[ -s "$file" ] || return 1
|
|
17
|
-
grep -Eq
|
|
18
|
-
grep -Eq
|
|
19
|
-
grep -Eq
|
|
20
|
-
grep -Eq
|
|
21
|
-
grep -Eq
|
|
24
|
+
grep -Eq "${HEADING2}Implementation Notes[[:space:]]*$" "$file" &&
|
|
25
|
+
grep -Eq "${HEADING3}Design Decisions[[:space:]]*$" "$file" &&
|
|
26
|
+
grep -Eq "${HEADING3}Deviations[[:space:]]*$" "$file" &&
|
|
27
|
+
grep -Eq "${HEADING3}Tradeoffs[[:space:]]*$" "$file" &&
|
|
28
|
+
grep -Eq "${HEADING3}Open Questions[[:space:]]*$" "$file"
|
|
22
29
|
}
|
|
23
30
|
|
|
24
31
|
has_worker_report() {
|