@walwal-harness/cli 6.1.5 → 6.1.6
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 +12 -0
- package/assets/launchd/com.walwal.harness-wake.plist.template +1 -1
- package/assets/templates/company-pipeline-manifest.json +71 -0
- package/assets/templates/config.json +4 -0
- package/bin/init.js +1 -0
- package/conventions/shared.md +18 -0
- package/package.json +1 -1
- package/scripts/conductor-tick.sh +97 -4
- package/scripts/harness-hourly-review.sh +284 -5
- package/scripts/harness-parity-audit.sh +101 -0
- package/scripts/harness-queue-manager.sh +1 -1
- package/scripts/harness-service-ops-monitor.sh +48 -3
- package/scripts/harness-wake-install.sh +1 -0
- package/scripts/harness-wake.sh +100 -8
- package/scripts/harness-worker-dispatch.sh +52 -21
- package/scripts/lib/harness-agent-resolver.sh +127 -0
package/CHANGELOG.md
CHANGED
|
@@ -29,6 +29,18 @@ docmeta:
|
|
|
29
29
|
|
|
30
30
|
# Changelog
|
|
31
31
|
|
|
32
|
+
## Unreleased
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- Hourly wake can now run either `claude -p` or `codex exec` via `HARNESS_WAKE_EXECUTOR=claude|codex`.
|
|
36
|
+
- Hourly wake supports `HARNESS_WAKE_MODE=headless|tmux|record`; `tmux` creates a separate time-boxed session per hourly company tick for easier inspection.
|
|
37
|
+
- Deterministic conductor fallback runs after each hourly review so meeting decisions can advance even when the LLM runtime is unavailable.
|
|
38
|
+
- Hourly meeting minutes now split meaningful progress from paperwork-only artifacts and track strategy cadence drift.
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
- launchd wake PATH now includes `$HOME/.local/bin` so local Claude/Codex installs are found from non-interactive jobs.
|
|
42
|
+
- Service-Ops records repeated incident signatures and marks repeated incidents as `recovery_required`, causing hourly meetings to route to CTO runtime recovery instead of repeating discussion.
|
|
43
|
+
|
|
32
44
|
## 6.2.0 — Always-on company runtime + legacy purge (2026-05-08)
|
|
33
45
|
|
|
34
46
|
### Why
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
<key>EnvironmentVariables</key>
|
|
27
27
|
<dict>
|
|
28
28
|
<key>PATH</key>
|
|
29
|
-
<string
|
|
29
|
+
<string>{{HOME}}/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
30
30
|
</dict>
|
|
31
31
|
</dict>
|
|
32
32
|
</plist>
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"description": "Canonical walwal company pipeline surface. Project-specific services, goals, tokens, and domain scripts may differ; company-loop mechanics must not.",
|
|
4
|
+
"identical_files": [
|
|
5
|
+
"scripts/conductor-tick.sh",
|
|
6
|
+
"scripts/harness-hourly-review.sh",
|
|
7
|
+
"scripts/harness-service-ops-monitor.sh",
|
|
8
|
+
"scripts/harness-wake.sh",
|
|
9
|
+
"scripts/harness-meeting-doc.sh",
|
|
10
|
+
"scripts/harness-next.sh",
|
|
11
|
+
"scripts/harness-worker-dispatch.sh",
|
|
12
|
+
"scripts/harness-stop.sh",
|
|
13
|
+
"scripts/harness-session-start.sh",
|
|
14
|
+
"scripts/harness-progress-set.sh",
|
|
15
|
+
"scripts/harness-queue-manager.sh",
|
|
16
|
+
"scripts/lib/harness-render-progress.sh",
|
|
17
|
+
"scripts/lib/harness-progress-migrate.sh",
|
|
18
|
+
"scripts/lib/harness-guardrail.sh",
|
|
19
|
+
"scripts/lib/harness-audit.sh",
|
|
20
|
+
"scripts/lib/harness-keywait.sh",
|
|
21
|
+
"conventions/conductor.md",
|
|
22
|
+
"conventions/meeting-manager.md",
|
|
23
|
+
"conventions/planner.md",
|
|
24
|
+
"conventions/cto.md",
|
|
25
|
+
"conventions/cqo.md",
|
|
26
|
+
"conventions/service-ops.md",
|
|
27
|
+
"conventions/dispatcher.md",
|
|
28
|
+
"gotchas/conductor.md",
|
|
29
|
+
"gotchas/meeting-manager.md",
|
|
30
|
+
"gotchas/planner.md",
|
|
31
|
+
"gotchas/cto.md",
|
|
32
|
+
"gotchas/cqo.md",
|
|
33
|
+
"gotchas/service-ops.md"
|
|
34
|
+
],
|
|
35
|
+
"template_mappings": [
|
|
36
|
+
{
|
|
37
|
+
"source": "assets/templates/HARNESS.md",
|
|
38
|
+
"target": ".harness/HARNESS.md",
|
|
39
|
+
"mode": "identical"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"config_core_delete_paths": [
|
|
43
|
+
".project",
|
|
44
|
+
".name",
|
|
45
|
+
".description",
|
|
46
|
+
".paths",
|
|
47
|
+
".production",
|
|
48
|
+
".telegram",
|
|
49
|
+
".services",
|
|
50
|
+
".owners",
|
|
51
|
+
".runtime",
|
|
52
|
+
".tech_stack",
|
|
53
|
+
".integrations"
|
|
54
|
+
],
|
|
55
|
+
"allowed_project_overrides": [
|
|
56
|
+
".harness/actions/**",
|
|
57
|
+
".harness/archive/**",
|
|
58
|
+
".harness/ops/**",
|
|
59
|
+
".harness/progress.json",
|
|
60
|
+
".harness/config.json project/service paths",
|
|
61
|
+
".harness/conventions/shared.md project-domain additions",
|
|
62
|
+
".harness/gotchas/* project-domain additions",
|
|
63
|
+
"scripts/harness-coo-research.sh",
|
|
64
|
+
"scripts/harness-coo-cadence-check.sh",
|
|
65
|
+
"scripts/harness-telegram-report.sh",
|
|
66
|
+
"scripts/harness-telegram-send.sh",
|
|
67
|
+
"scripts/check-real-time-logs.sh",
|
|
68
|
+
"scripts/test-*.sh",
|
|
69
|
+
"scripts/tests/**"
|
|
70
|
+
]
|
|
71
|
+
}
|
|
@@ -356,6 +356,10 @@
|
|
|
356
356
|
"worker_concurrency": 3,
|
|
357
357
|
"worker_spawn": "claude",
|
|
358
358
|
"worker_spawn_description": "claude 이면 idle worker 배정 시 `claude -p` 백그라운드 프로세스를 띄운다. record 로 두면 prompt/log만 생성한다.",
|
|
359
|
+
"hourly_wake_executor": "claude",
|
|
360
|
+
"hourly_wake_executor_description": "claude 이면 `claude -p`, codex 이면 `codex exec` 로 매시간 autonomous tick 을 실행한다.",
|
|
361
|
+
"hourly_wake_mode": "headless",
|
|
362
|
+
"hourly_wake_mode_description": "headless 는 백그라운드 실행, tmux 는 매시간 별도 tmux 세션, record 는 prompt/log만 생성한다.",
|
|
359
363
|
"autonomous": true,
|
|
360
364
|
"parallel_by_default": true
|
|
361
365
|
},
|
package/bin/init.js
CHANGED
|
@@ -1357,6 +1357,7 @@ function runVerify() {
|
|
|
1357
1357
|
'generator-backend', 'generator-frontend', 'generator-designer', 'generator-devops',
|
|
1358
1358
|
'evaluator-code-quality', 'evaluator-functional', 'evaluator-visual',
|
|
1359
1359
|
'evaluator-architecture', 'evaluator-security',
|
|
1360
|
+
'coo-developer', 'documentationer',
|
|
1360
1361
|
'brainstorming',
|
|
1361
1362
|
];
|
|
1362
1363
|
const requiredFrontmatter = ['name', 'description'];
|
package/conventions/shared.md
CHANGED
|
@@ -23,6 +23,24 @@ docmeta:
|
|
|
23
23
|
|
|
24
24
|
<!-- 항목이 추가되면 아래에 기록됩니다 -->
|
|
25
25
|
|
|
26
|
+
### [C-SYS-AUTONOMOUS-DEPLOY-AFTER-FIX] 코드 수정 후 운영 반영은 회사가 자율 처리
|
|
27
|
+
|
|
28
|
+
- **Status**: verified
|
|
29
|
+
- **Date**: 2026-05-10
|
|
30
|
+
- **Scope**: all agents
|
|
31
|
+
- **Rule**: 어떤 에이전트든 코드를 수정한 직후 운영 반영이 필요하면 build, restart, migration, cron/launchd 재등록, health check, ops-report까지 회사가 자율 처리한다. CTO, Service-Ops, Generator-DevOps 사이 협의는 내부 handoff로 처리하고 Owner에게는 시작 통지와 결과 보고만 한다.
|
|
32
|
+
- **Why**: Owner 입력은 GOAL/사고/결과 확인용이지 운영 펌프 신호가 아니다. 최초 GOAL 이후 회사는 내부 owner를 정해 복구와 배포를 진행해야 한다.
|
|
33
|
+
- **Apply**: 코드/설정 변경 후 운영 반영이 필요하면 Meeting-Manager 또는 Conductor는 `required_execution`에 owner, deliverable, verifier를 명시하고, Service-Ops가 health evidence를 갱신할 때까지 `waiting_owner`로 멈추지 않는다.
|
|
34
|
+
|
|
35
|
+
### [C-SYS-HOURLY-REPORT-AS-MEETING-MINUTES] hourly 보고서는 부서 회의록 형식이다
|
|
36
|
+
|
|
37
|
+
- **Status**: verified
|
|
38
|
+
- **Date**: 2026-05-10
|
|
39
|
+
- **Scope**: meeting-manager, service-ops, conductor
|
|
40
|
+
- **Rule**: `harness-hourly-review.sh`가 작성하는 md 및 Telegram 송신 대상은 단순 service health/KPI dump가 아니라 CEO/COO/CTO/CQO/Service-Ops의 role position, discussion, decision JSON, action items, required execution을 포함한 회의록이어야 한다.
|
|
41
|
+
- **Why**: Owner가 확인하려는 것은 서버 운용현황 자체가 아니라 회사가 GOAL을 향해 어떤 상황을 어떻게 타진하고 어떤 실행 결정을 내렸는지다.
|
|
42
|
+
- **Anti-pattern**: health table만 있고 `required_execution`, owner, verifier, next-tick deliverable이 없으면 회의가 아니라 paperwork로 분류한다.
|
|
43
|
+
|
|
26
44
|
### [C-SYS-EXEC-ROLE-CONTRACT] 회사 구조와 임원 역할 계약은 하네스 공통 규칙이다
|
|
27
45
|
|
|
28
46
|
- **Status**: verified
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "6.1.
|
|
3
|
+
"version": "6.1.6",
|
|
4
4
|
"description": "Production harness for AI agent engineering — NEXUS-adapted company metaphor (Dispatcher/CEO + Conductor + Meeting-Manager + COO/Planner + CTO + CQO + Service-Ops). Always-on company mode, parallel worker pool, Brainstormer, Planner, Generator(BE/FE/Designer/DevOps), Evaluator chain (Code-Quality → Functional → Visual + Architecture/Security). Supports React, Next.js, and Flutter FE stacks.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|
|
@@ -33,6 +33,7 @@ planner_requested_mode=$(jq -r '.planner.requested_mode // "null"' "$PROGRESS")
|
|
|
33
33
|
planner_last_brief=$(jq -r '.planner.last_brief // "null"' "$PROGRESS")
|
|
34
34
|
goal_adherence=$(jq -r '.goals.current_adherence // "null"' "$PROGRESS")
|
|
35
35
|
meetings_active_count=$(jq -r '(.meetings.active | length) // 0' "$PROGRESS")
|
|
36
|
+
pending_required_execution=$(jq -r 'if (.meetings.decision.required_execution // null) != null then "true" else "false" end' "$PROGRESS")
|
|
36
37
|
cqo_verdict=$(jq -r '.cqo.sprint_verdict // "pending"' "$PROGRESS")
|
|
37
38
|
ops_report=$(jq -r '.service_ops.auto_retro.last_report // "null"' "$PROGRESS")
|
|
38
39
|
cto_hotfixes=$(jq -r '.cto.open_hotfixes // 0' "$PROGRESS")
|
|
@@ -120,6 +121,10 @@ ensure_team_queue() {
|
|
|
120
121
|
|
|
121
122
|
load_meeting_decision() {
|
|
122
123
|
local decision
|
|
124
|
+
if jq -e '(.meetings.decision.required_execution // null) != null' "$PROGRESS" >/dev/null 2>&1; then
|
|
125
|
+
jq -c '.meetings.decision' "$PROGRESS"
|
|
126
|
+
return 0
|
|
127
|
+
fi
|
|
123
128
|
decision=$(bash "$SCRIPT_DIR/harness-meeting-doc.sh" "$PROJECT_ROOT" read-decision 2>/dev/null || true)
|
|
124
129
|
if [ -n "$decision" ]; then
|
|
125
130
|
echo "$decision"
|
|
@@ -135,6 +140,47 @@ dispatch_company_workers() {
|
|
|
135
140
|
bash "$SCRIPT_DIR/harness-worker-dispatch.sh" "$PROJECT_ROOT" 2>/dev/null || echo "[]"
|
|
136
141
|
}
|
|
137
142
|
|
|
143
|
+
ensure_strategy_work_package() {
|
|
144
|
+
local required_json="$1"
|
|
145
|
+
local deliverable_path="$2"
|
|
146
|
+
local target_path="$PROJECT_ROOT/$deliverable_path"
|
|
147
|
+
local stamp
|
|
148
|
+
stamp="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
149
|
+
|
|
150
|
+
[ -n "$deliverable_path" ] && [ "$deliverable_path" != "null" ] || return 0
|
|
151
|
+
mkdir -p "$(dirname "$target_path")"
|
|
152
|
+
if [ ! -f "$target_path" ]; then
|
|
153
|
+
{
|
|
154
|
+
echo "# Strategy Cadence Recovery Work Package"
|
|
155
|
+
echo ""
|
|
156
|
+
echo "- created_at: $stamp"
|
|
157
|
+
echo "- owner: Planner/COO"
|
|
158
|
+
echo "- source_decision: $(jq -r '.source_path // "unknown"' <<<"$required_json")"
|
|
159
|
+
echo "- required_execution_id: $(jq -r '.id // "unknown"' <<<"$required_json")"
|
|
160
|
+
echo "- status: opened"
|
|
161
|
+
echo ""
|
|
162
|
+
echo "## Candidate Idea"
|
|
163
|
+
echo ""
|
|
164
|
+
echo "Planner/COO must define one concrete strategy candidate for this hour. A restatement of the goal is not sufficient."
|
|
165
|
+
echo ""
|
|
166
|
+
echo "## Data Source"
|
|
167
|
+
echo ""
|
|
168
|
+
echo "Name the market data source, lookback window, and any filters used by the candidate."
|
|
169
|
+
echo ""
|
|
170
|
+
echo "## Backtest Command"
|
|
171
|
+
echo ""
|
|
172
|
+
echo '```bash'
|
|
173
|
+
echo "# Fill with the exact command that produces a backtest result artifact."
|
|
174
|
+
echo '```'
|
|
175
|
+
echo ""
|
|
176
|
+
echo "## CQO Acceptance"
|
|
177
|
+
echo ""
|
|
178
|
+
echo "- PASS requires a generated candidate artifact and a backtest/evaluation artifact."
|
|
179
|
+
echo "- If no candidate is generated before the next hourly review, classify the cycle as paperwork_only_failure."
|
|
180
|
+
} > "$target_path"
|
|
181
|
+
fi
|
|
182
|
+
}
|
|
183
|
+
|
|
138
184
|
effective_mode="$(normalize_company_mode)"
|
|
139
185
|
ensure_team_queue
|
|
140
186
|
drift_classification="$(infer_drift_classification)"
|
|
@@ -260,7 +306,7 @@ elif [ "$parallel_pending_count" -gt 0 ] && [ "$agent_status" = "completed" ] &&
|
|
|
260
306
|
.conductor.rendezvous = null"
|
|
261
307
|
fi
|
|
262
308
|
|
|
263
|
-
elif [ "$requested_mode" != "null" ] && [ -n "$requested_mode" ] && [ "$current_agent" != "service-ops" ]; then
|
|
309
|
+
elif [ "$pending_required_execution" != "true" ] && [ "$requested_mode" != "null" ] && [ -n "$requested_mode" ] && [ "$current_agent" != "service-ops" ]; then
|
|
264
310
|
next="service-ops"
|
|
265
311
|
action="spawn:service-ops:${requested_mode}"
|
|
266
312
|
|
|
@@ -292,6 +338,8 @@ elif [ "$current_agent" = "meeting-manager" ] && [ "$agent_status" = "completed"
|
|
|
292
338
|
evidence=$(jq -c '.evidence // []' <<<"$meeting_decision")
|
|
293
339
|
decision_drift=$(jq -r '.drift_classification // "unknown"' <<<"$meeting_decision")
|
|
294
340
|
source_path=$(jq -r '.source_path // "null"' <<<"$meeting_decision")
|
|
341
|
+
required_execution=$(jq -c '.required_execution // null' <<<"$meeting_decision")
|
|
342
|
+
required_deliverable=$(jq -r '.required_execution.deliverable_path // "null"' <<<"$meeting_decision")
|
|
295
343
|
# v6.2 — tracks length >= 2 → fork-join, else single. mode 필드 없음.
|
|
296
344
|
decision_tracks=$(jq -c '.tracks // []' <<<"$meeting_decision")
|
|
297
345
|
decision_rendezvous=$(jq -c '.rendezvous // null' <<<"$meeting_decision")
|
|
@@ -308,10 +356,34 @@ elif [ "$current_agent" = "meeting-manager" ] && [ "$agent_status" = "completed"
|
|
|
308
356
|
esac
|
|
309
357
|
fi
|
|
310
358
|
|
|
359
|
+
if [ "$required_execution" = "null" ]; then
|
|
360
|
+
case "$owner:$action_type" in
|
|
361
|
+
planner:continue-current-handoff|planner:execution-plan|cto:continue-current-handoff|cqo:continue-current-handoff|generator-*:continue-current-handoff|evaluator-*:continue-current-handoff)
|
|
362
|
+
next="meeting-manager"
|
|
363
|
+
action="convene:followup-review:execution-contract-missing"
|
|
364
|
+
new_conductor_state="waiting_meeting"
|
|
365
|
+
new_workflow_stage="followup-review"
|
|
366
|
+
meeting_prepare=true
|
|
367
|
+
meeting_filter="
|
|
368
|
+
.meetings.active = [\"meeting-manager\"] |
|
|
369
|
+
.meetings.requested_type = \"followup-review\" |
|
|
370
|
+
.meetings.requested_reason = \"execution-contract-missing\" |
|
|
371
|
+
.meetings.contract_missing = {
|
|
372
|
+
\"owner\": \"$owner\",
|
|
373
|
+
\"action_type\": \"$action_type\",
|
|
374
|
+
\"source_path\": $(escape_json_string "$source_path"),
|
|
375
|
+
\"detected_at\": (now | todate)
|
|
376
|
+
}"
|
|
377
|
+
;;
|
|
378
|
+
esac
|
|
379
|
+
fi
|
|
380
|
+
|
|
311
381
|
# v6.2 — Parallel fork: if 2+ tracks, materialize all tracks as running.
|
|
312
382
|
# `next_agent` keeps the first owner only for backward-compatible handoff display;
|
|
313
383
|
# the fork state itself must expose every track as active in the same tick.
|
|
314
|
-
if [ "$
|
|
384
|
+
if [ "$action" = "convene:followup-review:execution-contract-missing" ]; then
|
|
385
|
+
:
|
|
386
|
+
elif [ "$(jq 'length' <<<"$decision_tracks")" -gt 1 ]; then
|
|
315
387
|
first_track_owner=$(jq -r '.[0].owner' <<<"$decision_tracks")
|
|
316
388
|
first_track_action=$(jq -r '.[0].action_type // "triage"' <<<"$decision_tracks")
|
|
317
389
|
first_track_id=$(jq -r '.[0].id // "track-1"' <<<"$decision_tracks")
|
|
@@ -355,6 +427,10 @@ elif [ "$current_agent" = "meeting-manager" ] && [ "$agent_status" = "completed"
|
|
|
355
427
|
.conductor.rendezvous = $decision_rendezvous |
|
|
356
428
|
.conductor.fork_meeting_id = (.meetings.current_id // null)"
|
|
357
429
|
else
|
|
430
|
+
if [ "$action_type" = "strategy-cadence-recovery" ]; then
|
|
431
|
+
ensure_strategy_work_package "$required_execution" "$required_deliverable"
|
|
432
|
+
owner="planner"
|
|
433
|
+
fi
|
|
358
434
|
next="$owner"
|
|
359
435
|
action="dispatch:${owner}:${action_type}"
|
|
360
436
|
new_workflow_stage="$(meeting_stage_for_owner "$owner")"
|
|
@@ -377,6 +453,7 @@ elif [ "$current_agent" = "meeting-manager" ] && [ "$agent_status" = "completed"
|
|
|
377
453
|
\"evidence\": $evidence,
|
|
378
454
|
\"drift_classification\": $(escape_json_string "$decision_drift"),
|
|
379
455
|
\"source_path\": $(escape_json_string "$source_path"),
|
|
456
|
+
\"required_execution\": $required_execution,
|
|
380
457
|
\"tracks\": [],
|
|
381
458
|
\"rendezvous\": null
|
|
382
459
|
} |
|
|
@@ -440,7 +517,17 @@ elif [ "$current_agent" = "coo-developer" ] && [ "$agent_status" = "completed" ]
|
|
|
440
517
|
fi
|
|
441
518
|
|
|
442
519
|
elif [ "$current_agent" = "cto" ]; then
|
|
443
|
-
|
|
520
|
+
cto_step_b_status=$(jq -r '.cto.step_b_status // ""' "$PROGRESS")
|
|
521
|
+
if [ "$agent_status" = "step-B-code-edited" ] || [[ "$cto_step_b_status" == *deferred* ]]; then
|
|
522
|
+
next="cqo"
|
|
523
|
+
action="spawn:cqo:gateway-typecheck-verify"
|
|
524
|
+
new_workflow_stage="quality-review"
|
|
525
|
+
meeting_filter='
|
|
526
|
+
.cqo.requested_mode = "gateway-typecheck-verify" |
|
|
527
|
+
.meetings.active = ["cqo"] |
|
|
528
|
+
.meetings.requested_type = null |
|
|
529
|
+
.meetings.requested_reason = null'
|
|
530
|
+
elif [ "${cto_hotfixes:-0}" -gt 0 ] || [ "${ops_recommendations:-0}" -gt 0 ]; then
|
|
444
531
|
next="planner"
|
|
445
532
|
action="dispatch:planner:execution-plan"
|
|
446
533
|
new_workflow_stage="coo-replan"
|
|
@@ -548,7 +635,13 @@ if [ "$next" = "meeting-manager" ] && [ "$workflow_stage" = "ops-review" ]; then
|
|
|
548
635
|
fi
|
|
549
636
|
|
|
550
637
|
worker_dispatches="[]"
|
|
551
|
-
if [ "$new_conductor_state" = "running" ] &&
|
|
638
|
+
if [ "$new_conductor_state" = "running" ] &&
|
|
639
|
+
[ "$next" != "meeting-manager" ] &&
|
|
640
|
+
[ "$next" != "service-ops" ] &&
|
|
641
|
+
[ "$next" != "planner" ] &&
|
|
642
|
+
[ "$next" != "cto" ] &&
|
|
643
|
+
[ "$next" != "cqo" ] &&
|
|
644
|
+
[ "$next" != "archive" ]; then
|
|
552
645
|
worker_dispatches="$(dispatch_company_workers)"
|
|
553
646
|
if jq -e 'type == "array" and length > 0' >/dev/null 2>&1 <<<"$worker_dispatches"; then
|
|
554
647
|
dispatch_count="$(jq 'length' <<<"$worker_dispatches")"
|