@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 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>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</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'];
@@ -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.5",
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 [ "$(jq 'length' <<<"$decision_tracks")" -gt 1 ]; then
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
- if [ "${cto_hotfixes:-0}" -gt 0 ] || [ "${ops_recommendations:-0}" -gt 0 ]; then
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" ] && [ "$next" != "meeting-manager" ] && [ "$next" != "service-ops" ] && [ "$next" != "archive" ]; then
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")"