@walwal-harness/cli 6.1.4 → 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 +40 -0
- package/README.md +43 -74
- package/assets/launchd/com.walwal.harness-wake.plist.template +32 -0
- package/assets/templates/CONVENTIONS.md +12 -0
- package/assets/templates/HARNESS.md +8 -8
- package/assets/templates/company-pipeline-manifest.json +71 -0
- package/assets/templates/config.json +29 -27
- package/assets/templates/memory.md +16 -2
- package/assets/templates/progress.json.template +6 -5
- package/bin/init.js +180 -90
- package/conventions/shared.md +34 -0
- package/gotchas/conductor.md +16 -0
- package/gotchas/dispatcher.md +16 -0
- package/gotchas/meeting-manager.md +8 -0
- package/gotchas/service-ops.md +8 -0
- package/package.json +4 -4
- package/scripts/conductor-tick.sh +157 -56
- package/scripts/harness-archive.sh +7 -5
- package/scripts/harness-dashboard-up.sh +11 -3
- package/scripts/harness-hourly-review.sh +582 -0
- package/scripts/harness-meeting-doc.sh +55 -16
- package/scripts/harness-next.sh +1 -1
- package/scripts/harness-parity-audit.sh +101 -0
- package/scripts/harness-queue-manager.sh +13 -6
- package/scripts/harness-service-ops-monitor.sh +273 -0
- package/scripts/harness-session-start.sh +60 -31
- package/scripts/harness-statusline.sh +8 -12
- package/scripts/harness-stop.sh +85 -0
- package/scripts/harness-user-prompt-submit.sh +11 -29
- package/scripts/harness-wake-install.sh +180 -0
- package/scripts/harness-wake.sh +350 -0
- package/scripts/harness-worker-dispatch.sh +188 -0
- package/scripts/lib/harness-agent-resolver.sh +127 -0
- package/scripts/lib/harness-progress-migrate.sh +6 -1
- package/scripts/lib/harness-render-progress.sh +3 -3
- package/skills/brainstorming/SKILL.md +2 -2
- package/skills/conductor/SKILL.md +67 -55
- package/skills/cqo/SKILL.md +1 -0
- package/skills/cto/SKILL.md +1 -0
- package/skills/dispatcher/SKILL.md +33 -14
- package/skills/evaluator-code-quality/SKILL.md +4 -4
- package/skills/evaluator-functional/SKILL.md +6 -6
- package/skills/evaluator-visual/SKILL.md +4 -4
- package/skills/generator-backend/SKILL.md +4 -4
- package/skills/generator-frontend/SKILL.md +5 -5
- package/skills/meeting-manager/SKILL.md +82 -12
- package/skills/planner/SKILL.md +3 -3
- package/skills/service-ops/SKILL.md +25 -0
- package/commands/harness-solo.md +0 -103
- package/commands/harness-stop.md +0 -53
- package/commands/harness-team.md +0 -530
- package/scripts/harness-dashboard.sh +0 -509
- package/scripts/harness-goal-init.sh +0 -72
- package/scripts/harness-goal-show.sh +0 -37
- package/scripts/harness-gotcha-memory.sh +0 -348
- package/scripts/harness-monitor.sh +0 -398
- package/scripts/harness-prompt-history.sh +0 -164
- package/scripts/harness-tmux.sh +0 -372
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-hourly-review.sh — deterministic hourly executive meeting record
|
|
3
|
+
#
|
|
4
|
+
# Every hourly batch must leave evidence. This writes executive meeting minutes
|
|
5
|
+
# and, for production services, runs Service-Ops monitoring first.
|
|
6
|
+
|
|
7
|
+
set -uo pipefail
|
|
8
|
+
|
|
9
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
10
|
+
source "$SCRIPT_DIR/lib/harness-render-progress.sh"
|
|
11
|
+
|
|
12
|
+
PROJECT_ROOT="$(resolve_harness_root "${1:-.}")" || exit 0
|
|
13
|
+
PROGRESS="$PROJECT_ROOT/.harness/progress.json"
|
|
14
|
+
CONFIG="$PROJECT_ROOT/.harness/config.json"
|
|
15
|
+
|
|
16
|
+
[ -f "$PROGRESS" ] || exit 0
|
|
17
|
+
[ -f "$CONFIG" ] || exit 0
|
|
18
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
19
|
+
|
|
20
|
+
if [ "$(jq -r '.conductor.state // "idle"' "$PROGRESS" 2>/dev/null || echo idle)" = "waiting_owner" ]; then
|
|
21
|
+
jq '
|
|
22
|
+
.conductor.state = "waiting_meeting" |
|
|
23
|
+
.conductor.current_action = "autonomous-normalize-waiting-owner" |
|
|
24
|
+
.next_agent = "meeting-manager" |
|
|
25
|
+
.agent_status = "pending" |
|
|
26
|
+
.meetings.active = ((.meetings.active // []) + ["meeting-manager"] | unique) |
|
|
27
|
+
.meetings.requested_type = (.meetings.requested_type // "followup-review") |
|
|
28
|
+
.meetings.requested_reason = (.meetings.requested_reason // "autonomous-normalize-waiting-owner") |
|
|
29
|
+
.workflow.stage = "ops-monitoring" |
|
|
30
|
+
.workflow.last_transition = (now | todate) |
|
|
31
|
+
.workflow.last_reason = "autonomous-normalize-waiting-owner"
|
|
32
|
+
' "$PROGRESS" > "${PROGRESS}.tmp" && mv "${PROGRESS}.tmp" "$PROGRESS"
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
ts="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
36
|
+
id="M-$(date -u +%Y%m%dT%H%M%SZ)-hourly"
|
|
37
|
+
stamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
|
38
|
+
meeting_dir="$PROJECT_ROOT/.harness/actions/meetings/$id"
|
|
39
|
+
meeting_rel=".harness/actions/meetings/$id/meeting-$id.md"
|
|
40
|
+
meeting_path="$PROJECT_ROOT/$meeting_rel"
|
|
41
|
+
log_path="$PROJECT_ROOT/.harness/progress.log"
|
|
42
|
+
|
|
43
|
+
mkdir -p "$meeting_dir"
|
|
44
|
+
|
|
45
|
+
ops_report=""
|
|
46
|
+
if [ -x "$SCRIPT_DIR/harness-service-ops-monitor.sh" ]; then
|
|
47
|
+
ops_report="$(bash "$SCRIPT_DIR/harness-service-ops-monitor.sh" "$PROJECT_ROOT" 2>/dev/null || true)"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
project_name="$(basename "$PROJECT_ROOT")"
|
|
51
|
+
goal_id="$(jq -r '.goals.active_id // "none"' "$PROGRESS" 2>/dev/null || echo none)"
|
|
52
|
+
current_agent="$(jq -r '.current_agent // "none"' "$PROGRESS" 2>/dev/null || echo none)"
|
|
53
|
+
next_agent="$(jq -r '.next_agent // "none"' "$PROGRESS" 2>/dev/null || echo none)"
|
|
54
|
+
agent_status="$(jq -r '.agent_status // "unknown"' "$PROGRESS" 2>/dev/null || echo unknown)"
|
|
55
|
+
conductor_state="$(jq -r '.conductor.state // "idle"' "$PROGRESS" 2>/dev/null || echo idle)"
|
|
56
|
+
conductor_action="$(jq -r '.conductor.current_action // "none"' "$PROGRESS" 2>/dev/null || echo none)"
|
|
57
|
+
tick_count="$(jq -r '.conductor.tick_count // 0' "$PROGRESS" 2>/dev/null || echo 0)"
|
|
58
|
+
health_count="$(jq -r '(.service_ops.health // []) | length' "$PROGRESS" 2>/dev/null || echo 0)"
|
|
59
|
+
alert_count="$(jq -r '[.service_ops.health[]? | select(.status == "down" or .status == "degraded")] | length' "$PROGRESS" 2>/dev/null || echo 0)"
|
|
60
|
+
warn_count="$(jq -r '[.service_ops.health[]? | select(.status == "warn" or .status == "log_missing")] | length' "$PROGRESS" 2>/dev/null || echo 0)"
|
|
61
|
+
queue_summary="not configured"
|
|
62
|
+
if [ -f "$PROJECT_ROOT/.harness/actions/feature-queue.json" ]; then
|
|
63
|
+
queue_summary="$(jq -r '"ready=\(.queue.ready|length), in_progress=\(.queue.in_progress|length), passed=\(.queue.passed|length), failed=\(.queue.failed|length)"' "$PROJECT_ROOT/.harness/actions/feature-queue.json" 2>/dev/null || echo "unreadable")"
|
|
64
|
+
fi
|
|
65
|
+
drift="$(jq -r '.service_ops.drift_classification // .meetings.decision.drift_classification // "unknown"' "$PROGRESS" 2>/dev/null || echo unknown)"
|
|
66
|
+
last_decision="$(jq -r '.meetings.last_decision // "none"' "$PROGRESS" 2>/dev/null || echo none)"
|
|
67
|
+
last_meeting="$(jq -r '.meetings.last_review_path // .meetings.current_record_path // "none"' "$PROGRESS" 2>/dev/null || echo none)"
|
|
68
|
+
cqo_audit="$(jq -r '.cqo.audit_path // empty' "$PROGRESS" 2>/dev/null || true)"
|
|
69
|
+
cto_review="$(jq -r '.cto.review_path // empty' "$PROGRESS" 2>/dev/null || true)"
|
|
70
|
+
open_incidents="$(jq -r '(.service_ops.incident.open // []) | length' "$PROGRESS" 2>/dev/null || echo 0)"
|
|
71
|
+
incident_repeat_count="$(jq -r '.service_ops.incident.repeat_count // 0' "$PROGRESS" 2>/dev/null || echo 0)"
|
|
72
|
+
incident_recovery_required="$(jq -r '.service_ops.incident.recovery_required // false' "$PROGRESS" 2>/dev/null || echo false)"
|
|
73
|
+
prev_required_json="$(jq -c '.meetings.decision.required_execution // .meetings.required_execution // null' "$PROGRESS" 2>/dev/null || echo null)"
|
|
74
|
+
prev_required_action="$(jq -r '(.meetings.decision.required_execution // .meetings.required_execution // {}).action // "none"' "$PROGRESS" 2>/dev/null || echo none)"
|
|
75
|
+
prev_required_owner="$(jq -r '(.meetings.decision.required_execution // .meetings.required_execution // {}).owner // "none"' "$PROGRESS" 2>/dev/null || echo none)"
|
|
76
|
+
prev_required_deliverable="$(jq -r '(.meetings.decision.required_execution // .meetings.required_execution // {}).deliverable_path // ""' "$PROGRESS" 2>/dev/null || true)"
|
|
77
|
+
prev_execution_result="none"
|
|
78
|
+
prev_execution_detail="no prior required_execution"
|
|
79
|
+
if [ "$prev_required_json" != "null" ]; then
|
|
80
|
+
if [ -n "$prev_required_deliverable" ] && [ -f "$PROJECT_ROOT/$prev_required_deliverable" ]; then
|
|
81
|
+
prev_execution_result="executed_unverified"
|
|
82
|
+
prev_execution_detail="deliverable exists: $prev_required_deliverable"
|
|
83
|
+
if [ "$prev_required_action" = "runtime-recovery-runbook" ] && [ "${open_incidents:-0}" -eq 0 ] && [ "$alert_count" -eq 0 ]; then
|
|
84
|
+
prev_execution_result="executed_and_verified"
|
|
85
|
+
prev_execution_detail="deliverable exists and Service-Ops has no open incident"
|
|
86
|
+
fi
|
|
87
|
+
elif [ "$prev_required_action" = "runtime-recovery-runbook" ] && [ "$(jq -r '.cto.step_b_status // .cto.step_a_status // ""' "$PROGRESS" 2>/dev/null || true)" != "" ]; then
|
|
88
|
+
prev_execution_result="attempted_blocked"
|
|
89
|
+
prev_execution_detail="CTO recovery state advanced but required deliverable missing: ${prev_required_deliverable:-unset}"
|
|
90
|
+
else
|
|
91
|
+
prev_execution_result="paperwork_only_failure"
|
|
92
|
+
prev_execution_detail="required deliverable missing: ${prev_required_deliverable:-unset}"
|
|
93
|
+
fi
|
|
94
|
+
fi
|
|
95
|
+
owner_escalation="false"
|
|
96
|
+
strategy_total_signals=0
|
|
97
|
+
strategy_previous_signals="$(jq -r '.goals.strategy_cadence.total_signals // 0' "$PROGRESS" 2>/dev/null || echo 0)"
|
|
98
|
+
strategy_delta=0
|
|
99
|
+
strategy_status="not_applicable"
|
|
100
|
+
strategy_target_per_hour=0
|
|
101
|
+
strategy_target_per_day=0
|
|
102
|
+
strategy_last_signal="none"
|
|
103
|
+
strategy_required=false
|
|
104
|
+
|
|
105
|
+
if grep -Eiq 'strategy|algo|backtest|신규.?전략|시간당.?1|일.?6|daily.?6|hourly.?1' "$log_path" "$PROJECT_ROOT/.harness/actions/goals.md" 2>/dev/null; then
|
|
106
|
+
strategy_required=true
|
|
107
|
+
strategy_target_per_hour=1
|
|
108
|
+
strategy_target_per_day=6
|
|
109
|
+
strategy_total_signals="$(grep -Eic '(^|[|[:space:]])(new-strategy|strategy-generated|algos?_candidate|backtest-jobs?|backtest_enqueue|신규.?전략|전략.?생성)([|[:space:]]|$)' "$log_path" 2>/dev/null || echo 0)"
|
|
110
|
+
strategy_last_signal="$(grep -Ei '(^|[|[:space:]])(new-strategy|strategy-generated|algos?_candidate|backtest-jobs?|backtest_enqueue|신규.?전략|전략.?생성)([|[:space:]]|$)' "$log_path" 2>/dev/null | tail -1 || true)"
|
|
111
|
+
[ -n "$strategy_last_signal" ] || strategy_last_signal="none"
|
|
112
|
+
if [ "$strategy_total_signals" -lt "$strategy_previous_signals" ]; then
|
|
113
|
+
strategy_previous_signals="$strategy_total_signals"
|
|
114
|
+
fi
|
|
115
|
+
strategy_delta=$((strategy_total_signals - strategy_previous_signals))
|
|
116
|
+
if [ "$strategy_delta" -ge "$strategy_target_per_hour" ]; then
|
|
117
|
+
strategy_status="on_track"
|
|
118
|
+
else
|
|
119
|
+
strategy_status="behind_goal"
|
|
120
|
+
fi
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
verdict="working"
|
|
124
|
+
if [ "$alert_count" -gt 0 ]; then
|
|
125
|
+
verdict="incident"
|
|
126
|
+
elif [ "$strategy_required" = "true" ] && [ "$strategy_status" = "behind_goal" ]; then
|
|
127
|
+
verdict="goal_drift"
|
|
128
|
+
elif [ "$conductor_state" = "escalated" ]; then
|
|
129
|
+
verdict="owner_needed"
|
|
130
|
+
elif [ "$conductor_state" = "waiting_owner" ]; then
|
|
131
|
+
verdict="autonomous_review"
|
|
132
|
+
elif [ "$current_agent" = "none" ] && [ "$next_agent" = "none" ]; then
|
|
133
|
+
verdict="idle"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
meeting_type="standup"
|
|
137
|
+
decision_owner="planner"
|
|
138
|
+
decision_action="execution-plan"
|
|
139
|
+
decision_rationale="정기 회의에서 active GOAL 대비 현재 작업과 운영 신호를 재정렬하고 다음 work package를 명확히 한다."
|
|
140
|
+
progress_meaningful_json='[]'
|
|
141
|
+
required_execution_json='null'
|
|
142
|
+
handoff_requires_execution=false
|
|
143
|
+
handoff_feature="$(printf "%s" "$conductor_action" | awk -F: '{print $NF}')"
|
|
144
|
+
case "$handoff_feature" in
|
|
145
|
+
""|none|null|pass-through|monitor|completed|running|ready|execution-plan|continue-current-handoff|goal-alignment)
|
|
146
|
+
handoff_feature="$stamp"
|
|
147
|
+
;;
|
|
148
|
+
esac
|
|
149
|
+
safe_handoff_feature="$(printf "%s" "$handoff_feature" | tr -cs 'A-Za-z0-9._-' '-' | sed 's/^-//; s/-$//')"
|
|
150
|
+
[ -n "$safe_handoff_feature" ] || safe_handoff_feature="$stamp"
|
|
151
|
+
|
|
152
|
+
case "$next_agent" in
|
|
153
|
+
planner|cto|cqo|generator-*|evaluator-*|documentationer|coo-developer)
|
|
154
|
+
if [ "$next_agent" != "$current_agent" ] || [[ "$conductor_action" == *handoff* ]] || [[ "$conductor_action" == advance:* ]] || [[ "$conductor_action" == spawn:* ]] || [[ "$conductor_action" == dispatch:* ]]; then
|
|
155
|
+
handoff_requires_execution=true
|
|
156
|
+
fi
|
|
157
|
+
;;
|
|
158
|
+
esac
|
|
159
|
+
case "$next_agent" in
|
|
160
|
+
planner|cto|cqo|service-ops|dispatcher)
|
|
161
|
+
decision_owner="$next_agent"
|
|
162
|
+
decision_action="continue-current-handoff"
|
|
163
|
+
decision_rationale="정기 회의에서 현재 handoff가 GOAL과 충돌하지 않음을 확인했으므로 예정된 내부 owner가 계속 진행한다."
|
|
164
|
+
;;
|
|
165
|
+
esac
|
|
166
|
+
if [ "$verdict" = "incident" ]; then
|
|
167
|
+
meeting_type="incident-war-room"
|
|
168
|
+
decision_owner="cto"
|
|
169
|
+
if [ "$incident_recovery_required" = "true" ] || [ "${incident_repeat_count:-0}" -ge 2 ]; then
|
|
170
|
+
decision_action="runtime-recovery-runbook"
|
|
171
|
+
decision_rationale="동일 운영 장애가 ${incident_repeat_count}회 연속 감지됐다. 반복 회의가 아니라 CTO가 복구 runbook을 실행하고 CQO가 evidence를 검증한다."
|
|
172
|
+
progress_meaningful_json='["runtime-recovery-runbook required"]'
|
|
173
|
+
required_execution_json="$(jq -n --arg id "RE-$stamp-runtime-recovery" --arg source "$meeting_rel" '{
|
|
174
|
+
id:$id,
|
|
175
|
+
owner:"cto",
|
|
176
|
+
action:"runtime-recovery-runbook",
|
|
177
|
+
deliverable_path:".harness/actions/cto-runbook-runtime-recovery-current.md",
|
|
178
|
+
verifier:"cqo+service-ops",
|
|
179
|
+
due:"next_tick",
|
|
180
|
+
source_path:$source,
|
|
181
|
+
success_condition:"Service-Ops verifies every configured production service as ok; partial recovery remains incident."
|
|
182
|
+
}')"
|
|
183
|
+
else
|
|
184
|
+
decision_action="runtime-recovery"
|
|
185
|
+
decision_rationale="Service-Ops가 down/degraded 신호를 발견했으므로 CTO가 복구 경로를 정하고 CQO가 복구 evidence를 검증한다."
|
|
186
|
+
progress_meaningful_json='["runtime-recovery decision"]'
|
|
187
|
+
required_execution_json="$(jq -n --arg id "RE-$stamp-runtime-recovery" --arg source "$meeting_rel" '{
|
|
188
|
+
id:$id,
|
|
189
|
+
owner:"cto",
|
|
190
|
+
action:"runtime-recovery",
|
|
191
|
+
deliverable_path:".harness/actions/cto-action-runtime-recovery-current.md",
|
|
192
|
+
verifier:"service-ops",
|
|
193
|
+
due:"next_tick",
|
|
194
|
+
source_path:$source,
|
|
195
|
+
success_condition:"Service-Ops health improves or records a concrete blocker."
|
|
196
|
+
}')"
|
|
197
|
+
fi
|
|
198
|
+
elif [ "$verdict" = "idle" ] || [ "$verdict" = "autonomous_review" ]; then
|
|
199
|
+
meeting_type="followup-review"
|
|
200
|
+
decision_owner="planner"
|
|
201
|
+
decision_action="goal-alignment"
|
|
202
|
+
decision_rationale="Owner 대기 없이 CEO/COO/CTO/CQO/Service-Ops가 GOAL 대비 다음 work package를 재정렬한다."
|
|
203
|
+
elif [ "$verdict" = "goal_drift" ]; then
|
|
204
|
+
meeting_type="spec-review"
|
|
205
|
+
decision_owner="planner"
|
|
206
|
+
decision_action="strategy-cadence-recovery"
|
|
207
|
+
decision_rationale="전략 생성 cadence가 목표보다 뒤처졌다. COO가 다음 시간의 전략 후보 생성/백테스트 work package를 즉시 재배정한다."
|
|
208
|
+
progress_meaningful_json='["strategy cadence recovery decision"]'
|
|
209
|
+
required_execution_json="$(jq -n --arg id "RE-$stamp-strategy-cadence" --arg source "$meeting_rel" --arg deliverable ".harness/actions/strategy-work-package-${stamp}.md" '{
|
|
210
|
+
id:$id,
|
|
211
|
+
owner:"planner",
|
|
212
|
+
action:"create-strategy-work-package",
|
|
213
|
+
deliverable_path:$deliverable,
|
|
214
|
+
verifier:"cqo",
|
|
215
|
+
due:"next_tick",
|
|
216
|
+
source_path:$source,
|
|
217
|
+
success_condition:"A strategy work package exists with candidate idea, data source, backtest command, and CQO acceptance criteria."
|
|
218
|
+
}')"
|
|
219
|
+
elif [ "$verdict" = "owner_needed" ]; then
|
|
220
|
+
meeting_type="all-hands"
|
|
221
|
+
decision_owner="dispatcher"
|
|
222
|
+
decision_action="escalate-owner"
|
|
223
|
+
decision_rationale="conductor가 escalated 상태이므로 CEO가 Owner escalation 필요성을 판정한다."
|
|
224
|
+
owner_escalation="true"
|
|
225
|
+
fi
|
|
226
|
+
|
|
227
|
+
if [ "$verdict" = "working" ] && [ "$handoff_requires_execution" = "true" ]; then
|
|
228
|
+
decision_owner="$next_agent"
|
|
229
|
+
case "$next_agent" in
|
|
230
|
+
planner)
|
|
231
|
+
decision_action="execution-plan"
|
|
232
|
+
deliverable_path=".harness/actions/planner-execution-plan-${safe_handoff_feature}.md"
|
|
233
|
+
verifier="cto+cqo"
|
|
234
|
+
success_condition="Planner creates a concrete execution plan that reconciles the current handoff with GOAL, queue priority, non-goals, owner, and verifier."
|
|
235
|
+
;;
|
|
236
|
+
cto)
|
|
237
|
+
decision_action="technical-handoff"
|
|
238
|
+
deliverable_path=".harness/actions/cto-action-${safe_handoff_feature}.md"
|
|
239
|
+
verifier="cqo"
|
|
240
|
+
success_condition="CTO records implementation scope, affected files, validation commands, blocker status, and the next execution owner."
|
|
241
|
+
;;
|
|
242
|
+
cqo)
|
|
243
|
+
decision_action="quality-verification"
|
|
244
|
+
deliverable_path=".harness/actions/cqo-audit-${safe_handoff_feature}.md"
|
|
245
|
+
verifier="meeting-manager"
|
|
246
|
+
success_condition="CQO records PASS/FAIL with evidence, regressions, and the next routing decision."
|
|
247
|
+
;;
|
|
248
|
+
generator-*)
|
|
249
|
+
decision_action="implement-${safe_handoff_feature}"
|
|
250
|
+
deliverable_path=".harness/actions/gen-report-${safe_handoff_feature}.md"
|
|
251
|
+
verifier="cqo+evaluator-code-quality"
|
|
252
|
+
success_condition="Generator produces the named implementation report, changed-file list, validation output, and handoff to CQO/evaluator."
|
|
253
|
+
;;
|
|
254
|
+
evaluator-*)
|
|
255
|
+
decision_action="verify-${safe_handoff_feature}"
|
|
256
|
+
deliverable_path=".harness/actions/evaluation-${safe_handoff_feature}.md"
|
|
257
|
+
verifier="cqo"
|
|
258
|
+
success_condition="Evaluator writes an evidence-backed PASS/FAIL report and does not rubber-stamp missing evidence."
|
|
259
|
+
;;
|
|
260
|
+
documentationer)
|
|
261
|
+
decision_action="document-${safe_handoff_feature}"
|
|
262
|
+
deliverable_path=".harness/actions/documentation-${safe_handoff_feature}.md"
|
|
263
|
+
verifier="planner"
|
|
264
|
+
success_condition="Documentationer writes the requested brief/report with source paths and decision impact."
|
|
265
|
+
;;
|
|
266
|
+
coo-developer)
|
|
267
|
+
decision_action="spike-${safe_handoff_feature}"
|
|
268
|
+
deliverable_path=".harness/actions/hypothesis/${safe_handoff_feature}/spike/report.md"
|
|
269
|
+
verifier="planner+cqo"
|
|
270
|
+
success_condition="COO developer records spike result, repro/evidence, and verdict input for Planner."
|
|
271
|
+
;;
|
|
272
|
+
*)
|
|
273
|
+
decision_action="execute-${safe_handoff_feature}"
|
|
274
|
+
deliverable_path=".harness/actions/execution-${safe_handoff_feature}.md"
|
|
275
|
+
verifier="meeting-manager"
|
|
276
|
+
success_condition="The owner writes a concrete deliverable and next-step evidence before the following hourly review."
|
|
277
|
+
;;
|
|
278
|
+
esac
|
|
279
|
+
decision_rationale="working 상태라도 현재 handoff는 산출물 없는 '계속 진행'으로 통과할 수 없다. 다음 tick까지 owner가 deliverable을 남기고 verifier가 검증한다."
|
|
280
|
+
progress_meaningful_json="$(jq -n --arg item "execution contract required: $next_agent/$decision_action" '[$item]')"
|
|
281
|
+
required_execution_json="$(jq -n \
|
|
282
|
+
--arg id "RE-$stamp-handoff" \
|
|
283
|
+
--arg owner "$decision_owner" \
|
|
284
|
+
--arg action "$decision_action" \
|
|
285
|
+
--arg deliverable "$deliverable_path" \
|
|
286
|
+
--arg verifier "$verifier" \
|
|
287
|
+
--arg source "$meeting_rel" \
|
|
288
|
+
--arg success "$success_condition" \
|
|
289
|
+
--arg conductor_action "$conductor_action" \
|
|
290
|
+
'{
|
|
291
|
+
id:$id,
|
|
292
|
+
owner:$owner,
|
|
293
|
+
action:$action,
|
|
294
|
+
deliverable_path:$deliverable,
|
|
295
|
+
verifier:$verifier,
|
|
296
|
+
due:"next_tick",
|
|
297
|
+
source_path:$source,
|
|
298
|
+
source_conductor_action:$conductor_action,
|
|
299
|
+
success_condition:$success
|
|
300
|
+
}')"
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
decision_json="$(jq -n \
|
|
304
|
+
--arg owner "$decision_owner" \
|
|
305
|
+
--arg action_type "$decision_action" \
|
|
306
|
+
--arg rationale "$decision_rationale" \
|
|
307
|
+
--arg drift "$drift" \
|
|
308
|
+
--arg source "$meeting_rel" \
|
|
309
|
+
--arg ops_report "$ops_report" \
|
|
310
|
+
--arg cqo_audit "$cqo_audit" \
|
|
311
|
+
--arg cto_review "$cto_review" \
|
|
312
|
+
--argjson progress_meaningful "$progress_meaningful_json" \
|
|
313
|
+
--argjson required_execution "$required_execution_json" \
|
|
314
|
+
--argjson owner_escalation "$owner_escalation" '
|
|
315
|
+
{
|
|
316
|
+
owner: $owner,
|
|
317
|
+
action_type: $action_type,
|
|
318
|
+
rationale: $rationale,
|
|
319
|
+
evidence: (
|
|
320
|
+
[
|
|
321
|
+
(if $ops_report != "" then {source:$ops_report, kind:"ops-report"} else empty end),
|
|
322
|
+
(if $cqo_audit != "" then {source:$cqo_audit, kind:"cqo-audit"} else empty end),
|
|
323
|
+
(if $cto_review != "" then {source:$cto_review, kind:"cto-review"} else empty end)
|
|
324
|
+
]
|
|
325
|
+
),
|
|
326
|
+
drift_classification: $drift,
|
|
327
|
+
source_path: $source,
|
|
328
|
+
owner_escalation: $owner_escalation,
|
|
329
|
+
progress_classification: {
|
|
330
|
+
meaningful_actions: $progress_meaningful,
|
|
331
|
+
paperwork_only: ["hourly-review", "service-ops-monitor"]
|
|
332
|
+
},
|
|
333
|
+
required_execution: $required_execution,
|
|
334
|
+
tracks: [],
|
|
335
|
+
rendezvous: null
|
|
336
|
+
}')"
|
|
337
|
+
meeting_json="$(jq -n \
|
|
338
|
+
--arg meeting_id "$id" \
|
|
339
|
+
--arg type "$meeting_type" \
|
|
340
|
+
--arg reason "$verdict" \
|
|
341
|
+
--arg source_path "$meeting_rel" \
|
|
342
|
+
--argjson decision "$decision_json" \
|
|
343
|
+
'{meeting_id:$meeting_id,type:$type,reason:$reason,decision:($decision + {source_path:$source_path})}')"
|
|
344
|
+
|
|
345
|
+
{
|
|
346
|
+
echo "# Hourly Executive Meeting Minutes"
|
|
347
|
+
echo ""
|
|
348
|
+
echo "- id: $id"
|
|
349
|
+
echo "- ts: $ts"
|
|
350
|
+
echo "- project: $project_name"
|
|
351
|
+
echo "- goal: $goal_id"
|
|
352
|
+
echo "- type: $meeting_type"
|
|
353
|
+
echo "- verdict: $verdict"
|
|
354
|
+
echo "- chair: Meeting-Manager"
|
|
355
|
+
echo "- attendees: Dispatcher/CEO, Planner/COO, CTO, CQO, Service-Ops"
|
|
356
|
+
echo ""
|
|
357
|
+
echo "## Evidence Read Before Discussion"
|
|
358
|
+
echo ""
|
|
359
|
+
echo "- conductor: $conductor_state"
|
|
360
|
+
echo "- tick_count: $tick_count"
|
|
361
|
+
echo "- current_agent: $current_agent ($agent_status)"
|
|
362
|
+
echo "- next_agent: $next_agent"
|
|
363
|
+
echo "- queue: $queue_summary"
|
|
364
|
+
echo "- drift_classification: $drift"
|
|
365
|
+
echo "- previous_decision: $last_decision"
|
|
366
|
+
echo "- previous_meeting: $last_meeting"
|
|
367
|
+
echo "- previous_required_execution: $prev_required_owner/$prev_required_action"
|
|
368
|
+
echo "- previous_execution_result: $prev_execution_result"
|
|
369
|
+
echo "- previous_execution_detail: $prev_execution_detail"
|
|
370
|
+
if [ -n "$ops_report" ]; then
|
|
371
|
+
echo "- service_ops_report: $ops_report"
|
|
372
|
+
fi
|
|
373
|
+
if [ -n "$cto_review" ]; then
|
|
374
|
+
echo "- cto_review: $cto_review"
|
|
375
|
+
fi
|
|
376
|
+
if [ -n "$cqo_audit" ]; then
|
|
377
|
+
echo "- cqo_audit: $cqo_audit"
|
|
378
|
+
fi
|
|
379
|
+
echo ""
|
|
380
|
+
echo "## Progress Classification"
|
|
381
|
+
echo ""
|
|
382
|
+
echo "- previous_required_execution_result: $prev_execution_result"
|
|
383
|
+
echo "- previous_required_execution_detail: $prev_execution_detail"
|
|
384
|
+
if [ "$verdict" = "incident" ] && { [ "$incident_recovery_required" = "true" ] || [ "${incident_repeat_count:-0}" -ge 2 ]; }; then
|
|
385
|
+
echo "- meaningful_actions: runtime-recovery-runbook required"
|
|
386
|
+
echo "- paperwork_only: hourly meeting minutes, Service-Ops monitor report"
|
|
387
|
+
elif [ "$verdict" = "goal_drift" ]; then
|
|
388
|
+
echo "- meaningful_actions: strategy cadence recovery decision"
|
|
389
|
+
echo "- paperwork_only: hourly meeting minutes, Service-Ops monitor report"
|
|
390
|
+
else
|
|
391
|
+
echo "- meaningful_actions: none detected in this deterministic batch until conductor/agent tick records execution"
|
|
392
|
+
echo "- paperwork_only: hourly meeting minutes, Service-Ops monitor report"
|
|
393
|
+
fi
|
|
394
|
+
echo ""
|
|
395
|
+
echo "## Role Briefs"
|
|
396
|
+
echo ""
|
|
397
|
+
echo "### Dispatcher/CEO"
|
|
398
|
+
case "$verdict" in
|
|
399
|
+
owner_needed)
|
|
400
|
+
echo "- 판단: escalation 상태이므로 Owner에게 외부 결정이 필요한지 확인한다."
|
|
401
|
+
;;
|
|
402
|
+
*)
|
|
403
|
+
echo "- 판단: 최초 GOAL 이후 Owner 입력은 interrupt다. 회사는 Owner를 기다리지 않고 GOAL 달성 루프를 계속한다."
|
|
404
|
+
;;
|
|
405
|
+
esac
|
|
406
|
+
echo "- 관심사: active GOAL, KPI, Owner-facing escalation 필요성."
|
|
407
|
+
echo ""
|
|
408
|
+
echo "### Planner/COO"
|
|
409
|
+
if [ "$verdict" = "idle" ] || [ "$verdict" = "autonomous_review" ]; then
|
|
410
|
+
echo "- 판단: work package가 멈췄거나 Owner 대기처럼 보이는 상태다. 다음 operating cycle을 재정렬해야 한다."
|
|
411
|
+
else
|
|
412
|
+
echo "- 판단: 현재 queue와 next_agent가 GOAL에 맞는지 유지 점검한다."
|
|
413
|
+
fi
|
|
414
|
+
echo "- 관심사: planning drift, hypothesis 필요성, 다음 work package 정의."
|
|
415
|
+
echo ""
|
|
416
|
+
echo "### CTO"
|
|
417
|
+
if [ "$alert_count" -gt 0 ]; then
|
|
418
|
+
echo "- 판단: 운영 장애가 구현/런타임 복구 대상인지 우선 판정하고 복구 owner를 지정해야 한다."
|
|
419
|
+
else
|
|
420
|
+
echo "- 판단: 현재 구현/아키텍처 흐름이 다음 handoff를 받을 수 있는지 확인한다."
|
|
421
|
+
fi
|
|
422
|
+
echo "- 관심사: implementation drift, runtime recovery, generator/evaluator 재배정."
|
|
423
|
+
echo ""
|
|
424
|
+
echo "### CQO"
|
|
425
|
+
echo "- 판단: 회의 결론은 evidence가 있는 항목만 통과시킨다. 복구/변경 후 회귀 검증 기준을 요구한다."
|
|
426
|
+
echo "- 관심사: quality drift, missing tests, PASS/FAIL 근거."
|
|
427
|
+
echo ""
|
|
428
|
+
echo "### Service-Ops"
|
|
429
|
+
echo ""
|
|
430
|
+
echo "- monitored_services: $health_count"
|
|
431
|
+
echo "- warnings: $warn_count"
|
|
432
|
+
echo "- alerts: $alert_count"
|
|
433
|
+
echo "- open_incidents: $open_incidents"
|
|
434
|
+
echo "- incident_repeat_count: $incident_repeat_count"
|
|
435
|
+
echo "- incident_recovery_required: $incident_recovery_required"
|
|
436
|
+
if [ "$strategy_required" = "true" ]; then
|
|
437
|
+
echo "- strategy_cadence: $strategy_status (delta=$strategy_delta, total_signals=$strategy_total_signals, target_per_hour=$strategy_target_per_hour, target_per_day=$strategy_target_per_day)"
|
|
438
|
+
echo "- last_strategy_signal: $strategy_last_signal"
|
|
439
|
+
fi
|
|
440
|
+
echo "- 판단: 운영 신호를 회의에 제출하고, 장애 시 incident-war-room을 요구한다."
|
|
441
|
+
echo ""
|
|
442
|
+
if [ "$health_count" -gt 0 ]; then
|
|
443
|
+
echo "| Service | Port | Health | Log | Status |"
|
|
444
|
+
echo "|---|---:|---|---|---|"
|
|
445
|
+
jq -r '(.service_ops.health // [])[] |
|
|
446
|
+
"| \(.name) | \(.host):\(.port) \(.port_state) | " +
|
|
447
|
+
(if .health_path == null then "n/a" else ((.health_status // "000")|tostring) + "/" + ((.expected_status // 200)|tostring) end) +
|
|
448
|
+
" | " +
|
|
449
|
+
(if (.log.configured|not) then "not configured" elif (.log.exists|not) then "missing" else ((.log.recent_errors|tostring) + " recent errors") end) +
|
|
450
|
+
" | \(.status) |"' "$PROGRESS"
|
|
451
|
+
else
|
|
452
|
+
echo "No production services are configured for Service-Ops monitoring."
|
|
453
|
+
fi
|
|
454
|
+
echo ""
|
|
455
|
+
echo "## Discussion"
|
|
456
|
+
echo ""
|
|
457
|
+
case "$verdict" in
|
|
458
|
+
incident)
|
|
459
|
+
echo "- Service-Ops: down/degraded 서비스가 있으므로 feature work보다 장애 triage가 우선이다."
|
|
460
|
+
if [ "$incident_recovery_required" = "true" ] || [ "${incident_repeat_count:-0}" -ge 2 ]; then
|
|
461
|
+
echo "- CTO: 같은 장애가 반복됐으므로 추가 회의가 아니라 runtime-recovery-runbook 실행으로 전환한다."
|
|
462
|
+
else
|
|
463
|
+
echo "- CTO: 복구 절차, 재시작, 설정 누락, 포트 충돌 중 원인을 좁히고 runtime-recovery work item을 받는다."
|
|
464
|
+
fi
|
|
465
|
+
echo "- CQO: 복구 완료 주장은 health check와 재현 가능한 evidence 없이는 PASS로 보지 않는다."
|
|
466
|
+
echo "- COO: 장애 복구가 GOAL 진행을 막는 범위를 queue에 반영한다."
|
|
467
|
+
echo "- CEO: Owner에게 묻지 않고 내부 복구 루프를 진행한다. 단 GOAL 변경이 필요하면 별도 escalation한다."
|
|
468
|
+
;;
|
|
469
|
+
goal_drift)
|
|
470
|
+
echo "- CEO: Owner가 없어도 최초 GOAL의 KPI를 기준으로 회사 루프를 평가한다."
|
|
471
|
+
echo "- COO: 전략 생성/백테스트 cadence가 목표보다 낮으므로 다음 work package를 전략 후보 생성으로 고정한다."
|
|
472
|
+
echo "- CTO: 필요한 데이터/실행 경로가 막혔는지 점검한다."
|
|
473
|
+
echo "- CQO: 후보 전략은 backtest evidence 없이는 유의미 진척으로 인정하지 않는다."
|
|
474
|
+
echo "- Service-Ops: 운영 장애가 cadence 저하 원인인지 계속 감시한다."
|
|
475
|
+
;;
|
|
476
|
+
owner_needed)
|
|
477
|
+
echo "- CEO: conductor escalation의 원인이 내부 해결 가능한지 우선 판정한다."
|
|
478
|
+
echo "- COO/CTO/CQO/Service-Ops: 각자 해결 가능한 내부 action을 제시하고, 불가능할 때만 Owner escalation을 유지한다."
|
|
479
|
+
;;
|
|
480
|
+
autonomous_review)
|
|
481
|
+
echo "- CEO: Owner 입력은 interrupt이므로 대기하지 않는다."
|
|
482
|
+
echo "- COO: 다음 work package를 재정렬한다."
|
|
483
|
+
echo "- CTO/CQO/Service-Ops: 구현, 검증, 운영 관점에서 다음 handoff를 준비한다."
|
|
484
|
+
;;
|
|
485
|
+
idle)
|
|
486
|
+
echo "- Meeting-Manager: idle은 Owner 대기가 아니라 operating cycle 재개 신호다."
|
|
487
|
+
echo "- COO: GOAL 대비 다음 후보 작업을 선정한다."
|
|
488
|
+
echo "- CTO/CQO/Service-Ops: 각자 실행/검증/운영 준비 상태를 점검한다."
|
|
489
|
+
;;
|
|
490
|
+
*)
|
|
491
|
+
echo "- 회의 결론: 현재 GOAL을 유지하고 다음 autonomous tick에서 conductor가 다음 부서를 배정한다."
|
|
492
|
+
;;
|
|
493
|
+
esac
|
|
494
|
+
echo ""
|
|
495
|
+
echo "## Decision JSON"
|
|
496
|
+
echo ""
|
|
497
|
+
echo '```json'
|
|
498
|
+
jq '.' <<<"$meeting_json"
|
|
499
|
+
echo '```'
|
|
500
|
+
echo ""
|
|
501
|
+
echo "## Action Items"
|
|
502
|
+
echo ""
|
|
503
|
+
case "$verdict" in
|
|
504
|
+
incident)
|
|
505
|
+
echo "- CTO: runtime recovery work item을 열고 down/degraded 서비스 복구 경로를 실행한다."
|
|
506
|
+
echo "- Service-Ops: 복구 후 monitor를 다시 돌려 last_check와 health evidence를 갱신한다."
|
|
507
|
+
echo "- CQO: 복구 evidence를 검증하고 regression risk를 기록한다."
|
|
508
|
+
;;
|
|
509
|
+
goal_drift)
|
|
510
|
+
echo "- Planner/COO: 다음 autonomous tick에서 전략 후보 생성/백테스트 work package를 발급한다."
|
|
511
|
+
echo "- CTO: 전략 실행 경로와 데이터 의존성 차단 여부를 점검한다."
|
|
512
|
+
echo "- CQO: backtest 결과가 없는 후보를 유의미 진척으로 집계하지 않는다."
|
|
513
|
+
;;
|
|
514
|
+
idle|autonomous_review)
|
|
515
|
+
echo "- Planner/COO: Owner 대기 없이 active GOAL 기준 다음 work package를 정의한다."
|
|
516
|
+
echo "- Conductor: meeting decision을 읽고 다음 부서를 자동 배정한다."
|
|
517
|
+
;;
|
|
518
|
+
*)
|
|
519
|
+
echo "- Conductor: 다음 autonomous tick에서 decision.owner/action_type 기준으로 handoff한다."
|
|
520
|
+
;;
|
|
521
|
+
esac
|
|
522
|
+
echo ""
|
|
523
|
+
echo "## Required Execution"
|
|
524
|
+
echo ""
|
|
525
|
+
if [ "$required_execution_json" = "null" ]; then
|
|
526
|
+
echo "- none"
|
|
527
|
+
else
|
|
528
|
+
jq -r '"- owner: \(.owner)\n- action: \(.action)\n- deliverable_path: \(.deliverable_path)\n- verifier: \(.verifier)\n- due: \(.due)\n- success_condition: \(.success_condition)"' <<<"$required_execution_json"
|
|
529
|
+
fi
|
|
530
|
+
} > "$meeting_path"
|
|
531
|
+
|
|
532
|
+
jq \
|
|
533
|
+
--arg ts "$ts" \
|
|
534
|
+
--arg id "$id" \
|
|
535
|
+
--arg rel "$meeting_rel" \
|
|
536
|
+
--arg verdict "$verdict" \
|
|
537
|
+
--arg type "$meeting_type" \
|
|
538
|
+
--arg strategy_status "$strategy_status" \
|
|
539
|
+
--arg strategy_last_signal "$strategy_last_signal" \
|
|
540
|
+
--arg prev_execution_result "$prev_execution_result" \
|
|
541
|
+
--arg prev_execution_detail "$prev_execution_detail" \
|
|
542
|
+
--argjson strategy_required "$strategy_required" \
|
|
543
|
+
--argjson strategy_target_per_hour "$strategy_target_per_hour" \
|
|
544
|
+
--argjson strategy_target_per_day "$strategy_target_per_day" \
|
|
545
|
+
--argjson strategy_total_signals "$strategy_total_signals" \
|
|
546
|
+
--argjson strategy_previous_signals "$strategy_previous_signals" \
|
|
547
|
+
--argjson strategy_delta "$strategy_delta" \
|
|
548
|
+
--argjson incident_recovery_required "$incident_recovery_required" \
|
|
549
|
+
--argjson decision "$decision_json" '
|
|
550
|
+
.meetings.last_type = "hourly-review" |
|
|
551
|
+
.meetings.last_reason = $verdict |
|
|
552
|
+
.meetings.last_review_at = $ts |
|
|
553
|
+
.meetings.last_review_path = $rel |
|
|
554
|
+
.meetings.current_id = $id |
|
|
555
|
+
.meetings.current_record_path = $rel |
|
|
556
|
+
.meetings.last_attendees = ["dispatcher","planner","cto","cqo","service-ops","meeting-manager"] |
|
|
557
|
+
.meetings.decision = $decision |
|
|
558
|
+
.meetings.previous_required_execution_result = $prev_execution_result |
|
|
559
|
+
.meetings.previous_required_execution_detail = $prev_execution_detail |
|
|
560
|
+
.meetings.required_execution = $decision.required_execution |
|
|
561
|
+
.goals.strategy_cadence = {
|
|
562
|
+
"required": $strategy_required,
|
|
563
|
+
"status": $strategy_status,
|
|
564
|
+
"target_per_hour": $strategy_target_per_hour,
|
|
565
|
+
"target_per_day": $strategy_target_per_day,
|
|
566
|
+
"total_signals": $strategy_total_signals,
|
|
567
|
+
"previous_total_signals": $strategy_previous_signals,
|
|
568
|
+
"delta_since_last_review": $strategy_delta,
|
|
569
|
+
"last_signal": $strategy_last_signal,
|
|
570
|
+
"last_check": $ts
|
|
571
|
+
} |
|
|
572
|
+
.service_ops.incident.recovery_required = $incident_recovery_required |
|
|
573
|
+
if $verdict == "incident" or $verdict == "autonomous_review" or $verdict == "idle" or $verdict == "goal_drift"
|
|
574
|
+
then .meetings.active = ((.meetings.active // []) + ["meeting-manager","dispatcher","planner","cto","cqo","service-ops"] | unique) |
|
|
575
|
+
.meetings.requested_type = (.meetings.requested_type // $type) |
|
|
576
|
+
.meetings.requested_reason = (.meetings.requested_reason // $verdict)
|
|
577
|
+
else . end |
|
|
578
|
+
.service_ops.monitor.last_hourly_review = $ts
|
|
579
|
+
' "$PROGRESS" > "${PROGRESS}.tmp" && mv "${PROGRESS}.tmp" "$PROGRESS"
|
|
580
|
+
|
|
581
|
+
echo "$ts | meeting-manager | hourly-review | $verdict | $id | $meeting_rel" >> "$log_path"
|
|
582
|
+
echo "$meeting_rel"
|