@walwal-harness/cli 7.1.39 → 7.1.41
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/HR-Resource/cdo/SKILL.md +9 -1
- package/HR-Resource/ceo/SKILL.md +33 -2
- package/HR-Resource/coo/SKILL.md +9 -1
- package/HR-Resource/cqo/SKILL.md +9 -1
- package/HR-Resource/cto/SKILL.md +10 -2
- package/HR-Resource/ops/SKILL.md +20 -2
- package/HR-Resource/ops-prompt-inspector/SKILL.md +105 -0
- package/HR-Resource/ops-prompt-inspector/agents/installation-agent.json +7 -0
- package/HR-Resource/ops-prompt-inspector/assets/PromptInspector.tsx +599 -0
- package/HR-Resource/ops-prompt-inspector/assets/prompt-inspector-static.js +180 -0
- package/HR-Resource/ops-prompt-inspector/references/handler-templates.md +255 -0
- package/HR-Resource/ops-prompt-inspector/scripts/discover_apis.py +509 -0
- package/HR-Resource/ops-prompt-inspector/scripts/install.sh +32 -0
- package/HR-Resource/ops-prompt-inspector/scripts/setup.py +381 -0
- package/apps/harness-dashboard/app/api/wake/route.ts +41 -0
- package/apps/harness-dashboard/components/Scene.tsx +568 -35
- package/apps/harness-dashboard/lib/__tests__/harness-state.test.ts +1 -202
- package/apps/harness-dashboard/lib/__tests__/sandbox-e2e.test.ts +490 -0
- package/apps/harness-dashboard/lib/harness-state.ts +107 -796
- package/apps/harness-dashboard/lib/types.ts +22 -222
- package/apps/harness-dashboard/playwright.config.ts +26 -7
- package/assets/templates/AGENTS-ko.md.template +1 -1
- package/assets/templates/AGENTS.md.template +12 -2
- package/assets/templates/HARNESS.md +2 -0
- package/assets/templates/company-pipeline-manifest.json +5 -14
- package/assets/templates/worker-report.md.template +38 -0
- package/bin/init.js +4 -3
- package/commands/goal.md +4 -2
- package/commands/hot-fix.md +1 -1
- package/commands/submission.md +1 -1
- package/package.json +1 -1
- package/scripts/conductor-tick.sh +12 -0
- package/scripts/harness-agenda.sh +105 -0
- package/scripts/harness-company-block.sh +89 -0
- package/scripts/harness-company-complete.sh +17 -8
- package/scripts/harness-company-cycle.sh +54 -0
- package/scripts/harness-progress-set.sh +8 -2
- package/scripts/harness-stop.sh +76 -0
- package/scripts/harness-wake-control.sh +151 -0
- package/scripts/harness-wake.sh +5 -0
- package/apps/harness-dashboard/components/Drawer.tsx +0 -146
- package/apps/harness-dashboard/components/Header.tsx +0 -40
- package/apps/harness-dashboard/components/MissionTimeline.tsx +0 -214
- package/apps/harness-dashboard/components/OrgTree.tsx +0 -459
- package/apps/harness-dashboard/components/drawer/AgentLogTab.tsx +0 -56
- package/apps/harness-dashboard/components/drawer/DesignPreviewTab.tsx +0 -160
- package/apps/harness-dashboard/components/drawer/GotchasTab.tsx +0 -89
- package/apps/harness-dashboard/components/drawer/HypothesisTab.tsx +0 -42
- package/apps/harness-dashboard/components/drawer/IncidentsTab.tsx +0 -50
- package/apps/harness-dashboard/components/drawer/MissionDocTab.tsx +0 -96
- package/apps/harness-dashboard/components/drawer/MissionFlowTab.tsx +0 -576
- package/apps/harness-dashboard/components/drawer/OwnerHistoryTab.tsx +0 -160
- package/apps/harness-dashboard/components/drawer/RoomMetricsTab.tsx +0 -90
- package/apps/harness-dashboard/components/drawer/TracksTab.tsx +0 -47
- package/apps/harness-dashboard/components/three/ArchiveBoxes.tsx +0 -95
- package/apps/harness-dashboard/components/three/DeptBoard.tsx +0 -175
- package/apps/harness-dashboard/components/three/Floor3D.tsx +0 -404
- package/apps/harness-dashboard/components/three/GoalCard3D.tsx +0 -123
- package/apps/harness-dashboard/components/three/HypothesisCards.tsx +0 -71
- package/apps/harness-dashboard/components/three/MeetingWhiteboard.tsx +0 -98
- package/apps/harness-dashboard/components/three/Minifig3D.tsx +0 -391
- package/apps/harness-dashboard/components/three/OpsSiren.tsx +0 -99
- package/apps/harness-dashboard/components/three/Stage3D.tsx +0 -104
- package/apps/harness-dashboard/components/three/TrackRibbons.tsx +0 -81
- package/apps/harness-dashboard/lib/__tests__/i18n.test.ts +0 -16
- package/apps/harness-dashboard/lib/__tests__/iso.test.ts +0 -42
- package/apps/harness-dashboard/lib/__tests__/state-mapping.test.ts +0 -183
- package/apps/harness-dashboard/lib/agent-roster.ts +0 -42
- package/apps/harness-dashboard/lib/furniture.ts +0 -165
- package/apps/harness-dashboard/lib/i18n.ts +0 -26
- package/apps/harness-dashboard/lib/iso.ts +0 -150
- package/apps/harness-dashboard/lib/markdown.tsx +0 -306
- package/apps/harness-dashboard/lib/path-planning.ts +0 -72
- package/apps/harness-dashboard/lib/state-mapping.ts +0 -146
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-agenda.sh — shared executive agenda (안건) for the perpetual operating loop.
|
|
3
|
+
#
|
|
4
|
+
# The agenda is the meetup file every CXX co-writes. The Stop hook forces the
|
|
5
|
+
# CEO to adjudicate it: while items are open/decided the loop keeps running; when
|
|
6
|
+
# it is empty the CEO must run a status-briefing round (each CXX confirms its
|
|
7
|
+
# live deliverables still operate toward the goal and raises new items) before
|
|
8
|
+
# the loop may yield to the next hourly wake.
|
|
9
|
+
#
|
|
10
|
+
# Store: .harness/documents/<goal-rel>/agenda.json
|
|
11
|
+
# { items:[{id,raised_by,kind,title,evidence,status:open|decided|done,
|
|
12
|
+
# decision,decided_by,created_at,updated_at}],
|
|
13
|
+
# cycles:int, last_cycle_at, last_briefing_at }
|
|
14
|
+
#
|
|
15
|
+
# Usage:
|
|
16
|
+
# harness-agenda.sh <project-root> <goal-rel> raise <cxx> <kind> "<title>" ["<evidence>"]
|
|
17
|
+
# harness-agenda.sh <project-root> <goal-rel> decide <id> "<decision>" [<owner-cxx>]
|
|
18
|
+
# harness-agenda.sh <project-root> <goal-rel> close <id>
|
|
19
|
+
# harness-agenda.sh <project-root> <goal-rel> brief # stamp a briefing round
|
|
20
|
+
# harness-agenda.sh <project-root> <goal-rel> active-count # items not done
|
|
21
|
+
# harness-agenda.sh <project-root> <goal-rel> open-count # items needing CEO decision
|
|
22
|
+
# harness-agenda.sh <project-root> <goal-rel> list
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
27
|
+
PROJECT_ROOT="${1:-.}"
|
|
28
|
+
GOAL_REL="${2:-}"
|
|
29
|
+
CMD="${3:-}"
|
|
30
|
+
command -v jq >/dev/null 2>&1 || { echo "[agenda] jq required" >&2; exit 1; }
|
|
31
|
+
[ -n "$GOAL_REL" ] && [ -n "$CMD" ] || { echo "[agenda] usage: $0 <project-root> <goal-rel> <cmd> ..." >&2; exit 2; }
|
|
32
|
+
|
|
33
|
+
AGENDA_DIR="$PROJECT_ROOT/.harness/documents/$GOAL_REL"
|
|
34
|
+
AGENDA="$AGENDA_DIR/agenda.json"
|
|
35
|
+
mkdir -p "$AGENDA_DIR"
|
|
36
|
+
[ -f "$AGENDA" ] || echo '{"items":[],"cycles":0,"last_cycle_at":null,"last_briefing_at":null}' > "$AGENDA"
|
|
37
|
+
|
|
38
|
+
STRUCTURED_LIB="$SCRIPT_DIR/lib/harness-structured-log.sh"
|
|
39
|
+
[ -f "$STRUCTURED_LIB" ] && source "$STRUCTURED_LIB"
|
|
40
|
+
|
|
41
|
+
NOW="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
42
|
+
|
|
43
|
+
apply() { # jq-program ; reads from extra --arg pairs already in "$@"
|
|
44
|
+
local prog="$1"; shift
|
|
45
|
+
local tmp="$AGENDA.tmp.$$"
|
|
46
|
+
if jq "$@" "$prog" "$AGENDA" > "$tmp" 2>/dev/null; then
|
|
47
|
+
mv "$tmp" "$AGENDA"
|
|
48
|
+
else
|
|
49
|
+
rm -f "$tmp"; echo "[agenda] write failed" >&2; exit 1
|
|
50
|
+
fi
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
upsert_todo() { # owner kind title status id priority
|
|
54
|
+
declare -f harness_todo_upsert >/dev/null 2>&1 || return 0
|
|
55
|
+
local todo
|
|
56
|
+
todo=$(jq -nc \
|
|
57
|
+
--arg id "$5" --arg owner "$1" --arg kind "$2" --arg title "$3" \
|
|
58
|
+
--arg status "$4" --arg now "$NOW" --argjson priority "${6:-60}" '
|
|
59
|
+
{id:$id,owner:$owner,kind:$kind,title:$title,status:$status,priority:$priority,
|
|
60
|
+
command:"agenda",mission_path:null,required_artifacts:[],
|
|
61
|
+
created_at:$now,updated_at:$now,last_heartbeat_at:$now,blocked_reason:null}')
|
|
62
|
+
harness_todo_upsert "$PROJECT_ROOT" "$todo"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case "$CMD" in
|
|
66
|
+
raise)
|
|
67
|
+
CXX="${4:-ceo}"; KIND="${5:-other}"; TITLE="${6:-untitled}"; EVID="${7:-}"
|
|
68
|
+
N=$(jq '.items | length' "$AGENDA")
|
|
69
|
+
ID="A-$(date -u +%Y%m%dT%H%M%SZ)-$N"
|
|
70
|
+
apply '.items += [{id:$id,raised_by:$cxx,kind:$kind,title:$title,evidence:$evid,status:"open",decision:null,decided_by:null,created_at:$now,updated_at:$now}]' \
|
|
71
|
+
--arg id "$ID" --arg cxx "$CXX" --arg kind "$KIND" --arg title "$TITLE" --arg evid "$EVID" --arg now "$NOW"
|
|
72
|
+
upsert_todo "$CXX" "agenda" "[안건] $TITLE" "pending" "$ID" 70
|
|
73
|
+
echo "$ID"
|
|
74
|
+
;;
|
|
75
|
+
decide)
|
|
76
|
+
ID="${4:-}"; DECISION="${5:-}"; OWNER="${6:-}"
|
|
77
|
+
[ -n "$ID" ] || { echo "[agenda] decide needs <id>" >&2; exit 2; }
|
|
78
|
+
apply '.items |= map(if .id==$id then .status="decided" | .decision=$decision | .decided_by="ceo" | .updated_at=$now else . end)' \
|
|
79
|
+
--arg id "$ID" --arg decision "$DECISION" --arg now "$NOW"
|
|
80
|
+
[ -n "$OWNER" ] && upsert_todo "$OWNER" "agenda" "[결정] $DECISION" "active" "$ID-do" 70
|
|
81
|
+
echo "decided $ID"
|
|
82
|
+
;;
|
|
83
|
+
close)
|
|
84
|
+
ID="${4:-}"
|
|
85
|
+
[ -n "$ID" ] || { echo "[agenda] close needs <id>" >&2; exit 2; }
|
|
86
|
+
apply '.items |= map(if .id==$id then .status="done" | .updated_at=$now else . end)' \
|
|
87
|
+
--arg id "$ID" --arg now "$NOW"
|
|
88
|
+
echo "closed $ID"
|
|
89
|
+
;;
|
|
90
|
+
brief)
|
|
91
|
+
apply '.last_briefing_at=$now' --arg now "$NOW"
|
|
92
|
+
echo "briefing stamped"
|
|
93
|
+
;;
|
|
94
|
+
active-count)
|
|
95
|
+
jq '[.items[] | select(.status != "done")] | length' "$AGENDA"
|
|
96
|
+
;;
|
|
97
|
+
open-count)
|
|
98
|
+
jq '[.items[] | select(.status == "open")] | length' "$AGENDA"
|
|
99
|
+
;;
|
|
100
|
+
list)
|
|
101
|
+
jq -c '[.items[] | select(.status != "done")]' "$AGENDA"
|
|
102
|
+
;;
|
|
103
|
+
*)
|
|
104
|
+
echo "[agenda] unknown command: $CMD" >&2; exit 2 ;;
|
|
105
|
+
esac
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-company-block.sh — mark the current v7 company mission BLOCKED on
|
|
3
|
+
# external authority (the second legitimate stop condition; the first is
|
|
4
|
+
# harness-company-complete.sh).
|
|
5
|
+
#
|
|
6
|
+
# Usage:
|
|
7
|
+
# bash scripts/harness-company-block.sh <project-root> "<missing authority>"
|
|
8
|
+
#
|
|
9
|
+
# This is the explicit running -> blocked transition. The CEO runs it when the
|
|
10
|
+
# next action genuinely requires external authority the harness cannot infer or
|
|
11
|
+
# obtain (new credentials/secrets, payment approval, legal/business acceptance,
|
|
12
|
+
# unavailable production access, destructive data action, or a direct conflict
|
|
13
|
+
# with the Owner's stated direction). It records the named blocker, flips the
|
|
14
|
+
# runtime out of "running" so the Stop hook releases the turn, and marks the
|
|
15
|
+
# active mission-state lifecycle "blocked". It does NOT archive documents.
|
|
16
|
+
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
|
|
19
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
20
|
+
PROJECT_ROOT="${1:-.}"
|
|
21
|
+
REASON="${2:-awaiting external authority}"
|
|
22
|
+
|
|
23
|
+
PROGRESS="$PROJECT_ROOT/.harness/progress.json"
|
|
24
|
+
TODOS="$PROJECT_ROOT/.harness/todos/state.json"
|
|
25
|
+
[ -f "$PROGRESS" ] || {
|
|
26
|
+
echo "[company-block] not found: $PROGRESS" >&2
|
|
27
|
+
exit 1
|
|
28
|
+
}
|
|
29
|
+
command -v jq >/dev/null 2>&1 || {
|
|
30
|
+
echo "[company-block] jq is required" >&2
|
|
31
|
+
exit 1
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if ! bash "$SCRIPT_DIR/harness-progress-set.sh" "$PROJECT_ROOT" \
|
|
35
|
+
'.company_state.state = "idle" |
|
|
36
|
+
.conductor.state = "blocked" |
|
|
37
|
+
.conductor.current_action = ("blocked:" + $reason) |
|
|
38
|
+
.conductor.blocked_at = (now | todate) |
|
|
39
|
+
.conductor.tracks = [] |
|
|
40
|
+
.conductor.rendezvous = null |
|
|
41
|
+
.conductor.fork_meeting_id = null |
|
|
42
|
+
.current_agent = null |
|
|
43
|
+
.next_agent = "none" |
|
|
44
|
+
.agent_status = "blocked" |
|
|
45
|
+
.owner_prompt.status = "awaiting-authority" |
|
|
46
|
+
.owner_prompt.blocked_reason = $reason |
|
|
47
|
+
.owner_prompt.blocked_at = (now | todate) |
|
|
48
|
+
del(.conductor.completed_at) |
|
|
49
|
+
del(.owner_prompt.completed_at) |
|
|
50
|
+
del(.company_state.completed_at)' \
|
|
51
|
+
--arg reason "$REASON"; then
|
|
52
|
+
echo "[company-block] FAILED: progress.json transition did not apply (reason=$REASON). Runtime is NOT marked blocked." >&2
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
if [ -f "$TODOS" ]; then
|
|
57
|
+
tmp="$(mktemp)"
|
|
58
|
+
if jq --arg reason "$REASON" '
|
|
59
|
+
.owners |= with_entries(
|
|
60
|
+
.value |= map(
|
|
61
|
+
if (.status == "active" or .status == "pending")
|
|
62
|
+
then .status = "blocked" | .blocked_reason = $reason | .updated_at = (now | todate)
|
|
63
|
+
else .
|
|
64
|
+
end
|
|
65
|
+
)
|
|
66
|
+
)
|
|
67
|
+
' "$TODOS" > "$tmp" 2>/dev/null; then mv "$tmp" "$TODOS"; else rm -f "$tmp"; fi
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
DOCS="$PROJECT_ROOT/.harness/documents"
|
|
71
|
+
if [ -d "$DOCS" ]; then
|
|
72
|
+
find "$DOCS" -name mission-state.json -type f -print0 | while IFS= read -r -d '' state; do
|
|
73
|
+
active="$(jq -r '.active // false' "$state" 2>/dev/null || echo false)"
|
|
74
|
+
lifecycle="$(jq -r '.lifecycle // .status // "unknown"' "$state" 2>/dev/null || echo unknown)"
|
|
75
|
+
case "$lifecycle" in
|
|
76
|
+
closed|cancelled|superseded|complete|completed|blocked) continue ;;
|
|
77
|
+
esac
|
|
78
|
+
if [ "$active" = "true" ]; then
|
|
79
|
+
tmp="$(mktemp)"
|
|
80
|
+
jq --arg r "$REASON" '.lifecycle = "blocked" | .active = false | .blocked_reason = $r | .blocked_at = (now | todate)' "$state" > "$tmp" && mv "$tmp" "$state"
|
|
81
|
+
fi
|
|
82
|
+
done
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
if command -v node >/dev/null 2>&1 && [ -f "$SCRIPT_DIR/harness-activity-record.js" ]; then
|
|
86
|
+
node "$SCRIPT_DIR/harness-activity-record.js" "$PROJECT_ROOT" >/dev/null 2>&1 || true
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
echo "[company-block] marked blocked: $REASON"
|
|
@@ -25,12 +25,12 @@ command -v jq >/dev/null 2>&1 || {
|
|
|
25
25
|
exit 1
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
bash "$SCRIPT_DIR/harness-progress-set.sh" "$PROJECT_ROOT" \
|
|
28
|
+
if ! bash "$SCRIPT_DIR/harness-progress-set.sh" "$PROJECT_ROOT" \
|
|
29
29
|
'.company_state.state = "idle" |
|
|
30
30
|
.company_state.active_workers = 0 |
|
|
31
31
|
.company_state.completed_at = (now | todate) |
|
|
32
32
|
.conductor.state = "completed" |
|
|
33
|
-
.conductor.current_action = "complete:
|
|
33
|
+
.conductor.current_action = ("complete:" + $reason) |
|
|
34
34
|
.conductor.completed_at = (now | todate) |
|
|
35
35
|
.conductor.tracks = [] |
|
|
36
36
|
.conductor.rendezvous = null |
|
|
@@ -39,15 +39,22 @@ bash "$SCRIPT_DIR/harness-progress-set.sh" "$PROJECT_ROOT" \
|
|
|
39
39
|
.next_agent = "none" |
|
|
40
40
|
.agent_status = "completed" |
|
|
41
41
|
.owner_prompt.status = "completed" |
|
|
42
|
-
.owner_prompt.completed_at = (now | todate)
|
|
42
|
+
.owner_prompt.completed_at = (now | todate) |
|
|
43
|
+
del(.owner_prompt.blocked_reason) |
|
|
44
|
+
del(.owner_prompt.blocked_at) |
|
|
45
|
+
del(.conductor.blocked_at)' \
|
|
46
|
+
--arg reason "$REASON"; then
|
|
47
|
+
echo "[company-complete] FAILED: progress.json transition did not apply (reason=$REASON). Runtime is NOT marked complete." >&2
|
|
48
|
+
exit 1
|
|
49
|
+
fi
|
|
43
50
|
|
|
44
51
|
if [ -f "$TODOS" ]; then
|
|
45
52
|
tmp="$(mktemp)"
|
|
46
53
|
jq '
|
|
47
54
|
.owners |= with_entries(
|
|
48
55
|
.value |= map(
|
|
49
|
-
if (.status == "active" or .status == "pending" or .status == "paused")
|
|
50
|
-
then .status = "done" | .updated_at = (now | todate)
|
|
56
|
+
if (.status == "active" or .status == "pending" or .status == "paused" or .status == "blocked")
|
|
57
|
+
then .status = "done" | del(.blocked_reason) | .updated_at = (now | todate)
|
|
51
58
|
else .
|
|
52
59
|
end
|
|
53
60
|
)
|
|
@@ -61,11 +68,13 @@ if [ -d "$DOCS" ]; then
|
|
|
61
68
|
active="$(jq -r '.active // false' "$state" 2>/dev/null || echo false)"
|
|
62
69
|
lifecycle="$(jq -r '.lifecycle // .status // "unknown"' "$state" 2>/dev/null || echo unknown)"
|
|
63
70
|
case "$lifecycle" in
|
|
64
|
-
closed|cancelled|superseded|complete|
|
|
71
|
+
closed|cancelled|superseded|complete|completed) continue ;;
|
|
65
72
|
esac
|
|
66
|
-
|
|
73
|
+
# A mission left "blocked" (active=false) is resumed and completed here, so
|
|
74
|
+
# clear its blocker fields too — block -> complete must leave no stale state.
|
|
75
|
+
if [ "$active" = "true" ] || [ "$lifecycle" = "blocked" ]; then
|
|
67
76
|
tmp="$(mktemp)"
|
|
68
|
-
jq '.lifecycle = "complete" | .active = false | .completed_at = (now | todate)' "$state" > "$tmp" && mv "$tmp" "$state"
|
|
77
|
+
jq '.lifecycle = "complete" | .active = false | .completed_at = (now | todate) | del(.blocked_reason) | del(.blocked_at)' "$state" > "$tmp" && mv "$tmp" "$state"
|
|
69
78
|
fi
|
|
70
79
|
done
|
|
71
80
|
fi
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-company-cycle.sh — record an OPERATING heartbeat for a perpetual goal.
|
|
3
|
+
#
|
|
4
|
+
# This is the steady-state transition for a never-completing (Mode 1) goal: the
|
|
5
|
+
# CEO runs it at the end of an operating tick AFTER a status-briefing round
|
|
6
|
+
# surfaced no new agenda. It flips the runtime to "operating" (a non-terminal
|
|
7
|
+
# state the Stop hook treats as a clean yield to the next hourly wake), bumps the
|
|
8
|
+
# cycle counter, and records activity — it does NOT terminate the mission or the
|
|
9
|
+
# company loop. Finite goals use harness-company-complete.sh instead.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# bash scripts/harness-company-cycle.sh <project-root> <goal-rel> [reason]
|
|
13
|
+
|
|
14
|
+
set -uo pipefail
|
|
15
|
+
|
|
16
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
17
|
+
PROJECT_ROOT="${1:-.}"
|
|
18
|
+
GOAL_REL="${2:-}"
|
|
19
|
+
REASON="${3:-operating-heartbeat}"
|
|
20
|
+
|
|
21
|
+
PROGRESS="$PROJECT_ROOT/.harness/progress.json"
|
|
22
|
+
[ -f "$PROGRESS" ] || { echo "[company-cycle] not found: $PROGRESS" >&2; exit 1; }
|
|
23
|
+
command -v jq >/dev/null 2>&1 || { echo "[company-cycle] jq required" >&2; exit 1; }
|
|
24
|
+
|
|
25
|
+
if ! bash "$SCRIPT_DIR/harness-progress-set.sh" "$PROJECT_ROOT" \
|
|
26
|
+
'.company_state.state = "operating" |
|
|
27
|
+
.conductor.state = "operating" |
|
|
28
|
+
.conductor.current_action = ("operating:" + $reason) |
|
|
29
|
+
.conductor.tracks = [] |
|
|
30
|
+
.current_agent = null |
|
|
31
|
+
.next_agent = "none" |
|
|
32
|
+
.agent_status = "operating" |
|
|
33
|
+
.owner_prompt.status = "operating" |
|
|
34
|
+
.conductor.stop_chain_count = 0 |
|
|
35
|
+
.operating.cycles = ((.operating.cycles // 0) + 1) |
|
|
36
|
+
.operating.last_cycle_at = (now | todate) |
|
|
37
|
+
.operating.goal = $goal' \
|
|
38
|
+
--arg reason "$REASON" --arg goal "$GOAL_REL"; then
|
|
39
|
+
echo "[company-cycle] FAILED: progress.json operating heartbeat did not apply (goal=$GOAL_REL). Runtime is NOT marked operating." >&2
|
|
40
|
+
exit 1
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
# bump the per-goal agenda cycle counter too (best-effort)
|
|
44
|
+
AGENDA="$PROJECT_ROOT/.harness/documents/$GOAL_REL/agenda.json"
|
|
45
|
+
if [ -n "$GOAL_REL" ] && [ -f "$AGENDA" ]; then
|
|
46
|
+
tmp="$AGENDA.tmp.$$"
|
|
47
|
+
jq '.cycles = ((.cycles // 0) + 1) | .last_cycle_at = (now | todate)' "$AGENDA" > "$tmp" && mv "$tmp" "$AGENDA" || rm -f "$tmp"
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
if command -v node >/dev/null 2>&1 && [ -f "$SCRIPT_DIR/harness-activity-record.js" ]; then
|
|
51
|
+
node "$SCRIPT_DIR/harness-activity-record.js" "$PROJECT_ROOT" >/dev/null 2>&1 || true
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
echo "[company-cycle] operating heartbeat recorded: $REASON"
|
|
@@ -17,10 +17,16 @@ PROJECT_ROOT="${1:-.}"
|
|
|
17
17
|
FILTER="${2:-}"
|
|
18
18
|
|
|
19
19
|
if [ -z "$FILTER" ]; then
|
|
20
|
-
echo "[progress-set] usage: $0 <project-root> <jq_filter>" >&2
|
|
20
|
+
echo "[progress-set] usage: $0 <project-root> <jq_filter> [jq-args...]" >&2
|
|
21
21
|
exit 2
|
|
22
22
|
fi
|
|
23
23
|
|
|
24
|
+
# Any args after the filter are passed straight through to jq (e.g.
|
|
25
|
+
# `--arg reason "$REASON"`). Callers MUST inject untrusted/free-text values this
|
|
26
|
+
# way and reference them as jq $variables — never string-interpolate into the
|
|
27
|
+
# filter, which breaks on quotes and can silently fail.
|
|
28
|
+
shift 2 2>/dev/null || true
|
|
29
|
+
|
|
24
30
|
PROGRESS="$PROJECT_ROOT/.harness/progress.json"
|
|
25
31
|
|
|
26
32
|
if [ ! -f "$PROGRESS" ]; then
|
|
@@ -32,7 +38,7 @@ fi
|
|
|
32
38
|
FINAL_FILTER="$FILTER | .updated_at = (now | todate)"
|
|
33
39
|
|
|
34
40
|
TMP="${PROGRESS}.tmp.$$"
|
|
35
|
-
if jq "$FINAL_FILTER" "$PROGRESS" > "$TMP" 2>/dev/null; then
|
|
41
|
+
if jq "$@" "$FINAL_FILTER" "$PROGRESS" > "$TMP" 2>/dev/null; then
|
|
36
42
|
mv "$TMP" "$PROGRESS"
|
|
37
43
|
else
|
|
38
44
|
rm -f "$TMP"
|
package/scripts/harness-stop.sh
CHANGED
|
@@ -51,6 +51,76 @@ if [ "$HOOK_EVENT" = "Stop" ]; then
|
|
|
51
51
|
if [ "$STOP_HOOK_ACTIVE" = "true" ]; then exit 0; fi
|
|
52
52
|
fi
|
|
53
53
|
|
|
54
|
+
# ── v7 깨끗한 정지 short-circuit ──────────────────────────────
|
|
55
|
+
# 회사 루프의 정당한 정지 조건은 둘뿐이다: COMPLETE, 그리고 외부권한 BLOCKED.
|
|
56
|
+
# harness-company-complete.sh / harness-company-block.sh 가 이 terminal 런타임
|
|
57
|
+
# 상태를 기록한다. 그 상태를 여기서 존중해, 끝났는데도 "계속하라"고 채근하지 않고
|
|
58
|
+
# 턴을 깨끗이 종료한다. (/goal 직후 routing/running 상태에서는 발동하지 않는다.)
|
|
59
|
+
case "$CONDUCTOR_STATE" in
|
|
60
|
+
completed|idle|blocked) exit 0 ;;
|
|
61
|
+
esac
|
|
62
|
+
if [ "$AGENT_STATUS" = "blocked" ] || [ "$OWNER_PROMPT_STATUS" = "completed" ] || [ "$OWNER_PROMPT_STATUS" = "awaiting-authority" ]; then
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# ── v7 영구 운영(operating) + 완료 backstop ──────────────────
|
|
67
|
+
# .harness/documents 의 mission-state 를 스캔해 루프 종류를 분류한다:
|
|
68
|
+
# - operating mission 이 active → 영구 모드: 안건을 강제 구동하거나 wake 로 양보
|
|
69
|
+
# - 모든 mission 이 terminal → 유한 루프 종료: 완료 처리 + 깨끗한 정지
|
|
70
|
+
# (mission 문서가 없으면 순수 v6 프로젝트 → 미발동)
|
|
71
|
+
DOCS_DIR="$CWD/.harness/documents"
|
|
72
|
+
if [ "$TASK_STOP_ACTIVE" != "true" ] && [ -d "$DOCS_DIR" ] && \
|
|
73
|
+
{ [ "$CONDUCTOR_STATE" = "running" ] || [ "$CONDUCTOR_STATE" = "operating" ]; }; then
|
|
74
|
+
any_mission="false"; active_mission="false"; operating_active="false"; operating_rel=""
|
|
75
|
+
while IFS= read -r state; do
|
|
76
|
+
[ -n "$state" ] || continue
|
|
77
|
+
any_mission="true"
|
|
78
|
+
a=$(jq -r '.active // false' "$state" 2>/dev/null || echo false)
|
|
79
|
+
lc=$(jq -r '.lifecycle // .status // "unknown"' "$state" 2>/dev/null || echo unknown)
|
|
80
|
+
case "$lc" in
|
|
81
|
+
closed|cancelled|superseded|complete|completed|blocked) ;;
|
|
82
|
+
operating|monitoring)
|
|
83
|
+
if [ "$a" = "true" ]; then
|
|
84
|
+
active_mission="true"; operating_active="true"
|
|
85
|
+
rel="${state#"$DOCS_DIR"/}"; operating_rel="${rel%/mission-state.json}"
|
|
86
|
+
fi ;;
|
|
87
|
+
*) [ "$a" = "true" ] && active_mission="true" ;;
|
|
88
|
+
esac
|
|
89
|
+
done < <(find "$DOCS_DIR" -name mission-state.json -type f 2>/dev/null)
|
|
90
|
+
|
|
91
|
+
if [ "$operating_active" = "true" ]; then
|
|
92
|
+
active_agenda=0
|
|
93
|
+
if [ -x "$SCRIPT_DIR/harness-agenda.sh" ]; then
|
|
94
|
+
active_agenda=$(bash "$SCRIPT_DIR/harness-agenda.sh" "$CWD" "$operating_rel" active-count 2>/dev/null || echo 0)
|
|
95
|
+
fi
|
|
96
|
+
# operating tick 도 활동 샘플 1회 + stop_chain_count 증가
|
|
97
|
+
NEW_COUNT=$((STOP_CHAIN_COUNT + 1))
|
|
98
|
+
jq --argjson n "$NEW_COUNT" '.conductor.stop_chain_count=$n | .conductor.last_stop_chain_at=(now|todate)' "$PROGRESS" > "$PROGRESS.tmp.$$" 2>/dev/null && mv "$PROGRESS.tmp.$$" "$PROGRESS" || rm -f "$PROGRESS.tmp.$$"
|
|
99
|
+
if command -v node >/dev/null 2>&1 && [ -f "$SCRIPT_DIR/harness-activity-record.js" ]; then node "$SCRIPT_DIR/harness-activity-record.js" "$CWD" >/dev/null 2>&1 || true; fi
|
|
100
|
+
if [ "${active_agenda:-0}" -gt 0 ] 2>/dev/null; then
|
|
101
|
+
bash "$SCRIPT_DIR/harness-progress-set.sh" "$CWD" '.conductor.state="running"' >/dev/null 2>&1 || true
|
|
102
|
+
jq -nc --arg n "$active_agenda" --arg g "$operating_rel" '{decision:"block", reason:("영구 운영 루프: 활성 안건 \($n)건. CEO는 .harness/documents/\($g)/agenda.json 의 모든 open 안건을 `scripts/harness-agenda.sh . \($g) decide` 로 판단·라우팅하고, decided 안건은 담당 CXX가 hired worker로 실행·검증한 뒤 `... close` 할 때까지 진행하라. 안건이 남았는데 턴을 끝내지 말 것.")}'
|
|
103
|
+
exit 0
|
|
104
|
+
elif [ "$CONDUCTOR_STATE" = "operating" ]; then
|
|
105
|
+
exit 0 # 안건 0 + heartbeat 기록됨 → 다음 hourly wake 로 양보(깨끗한 정지)
|
|
106
|
+
else
|
|
107
|
+
jq -nc --arg g "$operating_rel" '{decision:"block", reason:("영구 운영 루프: 안건 없음. CEO는 각 CXX(COO/CDO/CTO/CQO/OPS)에게 현황 보고를 지시하라 — 각 CXX는 자기 산하 worker 산출물이 goal 기준으로 여전히 올바르게 동작하는지 브리핑하고, 손실·드리프트·기회·리스크가 보이면 `scripts/harness-agenda.sh . \($g) raise <cxx> <kind> \"<title>\"` 로 새 안건을 등록한다. 한 바퀴 브리핑이 정말 무안건이면 `scripts/harness-company-cycle.sh . \($g)` 로 operating heartbeat 를 남기고 다음 hourly wake 로 양보하라.")}'
|
|
108
|
+
exit 0
|
|
109
|
+
fi
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
if [ "$any_mission" = "true" ] && [ "$active_mission" = "false" ] && [ "$CONDUCTOR_STATE" = "running" ]; then
|
|
113
|
+
# Never silently complete: record why the backstop fired so an operator can
|
|
114
|
+
# audit a misclassification, and only treat the loop as finished if the
|
|
115
|
+
# transition actually applied (otherwise fall through and keep chaining).
|
|
116
|
+
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) | stop-hook | auto-complete | backstop fired (conductor=running but no active mission under .harness/documents)" >> "$CWD/.harness/progress.log" 2>/dev/null || true
|
|
117
|
+
if bash "$SCRIPT_DIR/harness-company-complete.sh" "$CWD" "loop-idle-no-active-mission" >/dev/null 2>&1; then
|
|
118
|
+
exit 0
|
|
119
|
+
fi
|
|
120
|
+
echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) | stop-hook | auto-complete | WARN: harness-company-complete.sh failed during backstop; continuing loop" >> "$CWD/.harness/progress.log" 2>/dev/null || true
|
|
121
|
+
fi
|
|
122
|
+
fi
|
|
123
|
+
|
|
54
124
|
# 자동 연쇄 조건:
|
|
55
125
|
# conductor/company loop 가 running
|
|
56
126
|
# AND task_stop 비활성
|
|
@@ -104,6 +174,12 @@ jq --argjson n "$NEW_COUNT" '.conductor.stop_chain_count = $n |
|
|
|
104
174
|
.conductor.last_stop_chain_at = (now | todate)' "$PROGRESS" > "$TMP" 2>/dev/null \
|
|
105
175
|
&& mv "$TMP" "$PROGRESS" || rm -f "$TMP"
|
|
106
176
|
|
|
177
|
+
# 자율 tick 마다 활동 샘플 1회 기록 → 대시보드 히트맵이 라이브 운영 중에도 채워진다.
|
|
178
|
+
# (recorder 의 runtimeIdle 게이트가 완료 후에는 count>0 샘플을 알아서 억제한다.)
|
|
179
|
+
if command -v node >/dev/null 2>&1 && [ -f "$SCRIPT_DIR/harness-activity-record.js" ]; then
|
|
180
|
+
node "$SCRIPT_DIR/harness-activity-record.js" "$CWD" >/dev/null 2>&1 || true
|
|
181
|
+
fi
|
|
182
|
+
|
|
107
183
|
# Claude 에게 다음 행동 지시
|
|
108
184
|
if [ "$ESCALATION" != "null" ]; then
|
|
109
185
|
REASON="자율 회사 루프 진행 중. escalation=${ESCALATION} 는 정지 사유가 아니라 meeting-manager/service-ops 공유 대상이다. current_agent=${CURRENT_AGENT}, next_agent=${NEXT_AGENT}, agent_status=${AGENT_STATUS}, conductor=${CONDUCTOR_STATE}, company=${COMPANY_STATE}. SKILL 에 따라 CEO/CXX/worker 흐름을 계속하거나 conductor-tick 을 굴려 meeting-manager 회의로 라우팅하라. progress.log 에는 절대 미래 시각을 쓰지 말 것. 진짜로 끝났을 때만 turn 을 종료하라."
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# harness-wake-control.sh — per-project hourly-wake launchd control, keyed by HARNESS_BASE_PORT.
|
|
3
|
+
#
|
|
4
|
+
# The perpetual operating loop (Mode 1) relies on the hourly launchd wake to
|
|
5
|
+
# resume cycles across sessions. This gives the dashboard a deterministic,
|
|
6
|
+
# per-project on/off handle: the launchd job Label is anchored to the project's
|
|
7
|
+
# HARNESS_BASE_PORT (e.g. com.walwal.harness-wake.43000), so each project's wake
|
|
8
|
+
# daemon is uniquely addressable and independently toggleable.
|
|
9
|
+
#
|
|
10
|
+
# launchd periodic jobs are StartInterval execs, not listening daemons, so there
|
|
11
|
+
# is no TCP port to bind — the base-port-keyed Label IS the fixed control id.
|
|
12
|
+
#
|
|
13
|
+
# Usage (all print JSON status except `label`):
|
|
14
|
+
# harness-wake-control.sh status <project-root>
|
|
15
|
+
# harness-wake-control.sh on <project-root> [--dry-run]
|
|
16
|
+
# harness-wake-control.sh off <project-root>
|
|
17
|
+
# harness-wake-control.sh label <project-root>
|
|
18
|
+
|
|
19
|
+
set -uo pipefail
|
|
20
|
+
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
22
|
+
WAKE_SH="$SCRIPT_DIR/harness-wake.sh"
|
|
23
|
+
INSTALL_SH="$SCRIPT_DIR/harness-wake-install.sh"
|
|
24
|
+
CMD="${1:-status}"
|
|
25
|
+
PROJECT_ROOT="${2:-$PWD}"
|
|
26
|
+
PROJECT_ROOT="$(cd "$PROJECT_ROOT" 2>/dev/null && pwd || echo "$PROJECT_ROOT")"
|
|
27
|
+
DRYRUN=0
|
|
28
|
+
[ "${3:-}" = "--dry-run" ] && DRYRUN=1
|
|
29
|
+
|
|
30
|
+
LAUNCH_AGENTS_DIR="$HOME/Library/LaunchAgents"
|
|
31
|
+
LOG_DIR="$HOME/.walwal-harness/logs"
|
|
32
|
+
PROJECTS_LIST="$HOME/.walwal-harness/projects.list"
|
|
33
|
+
STATE_DIR="$PROJECT_ROOT/.harness/runtime"
|
|
34
|
+
GLOBAL_LABEL="com.walwal.harness-wake"
|
|
35
|
+
DOMAIN="gui/$(id -u 2>/dev/null || echo 0)"
|
|
36
|
+
|
|
37
|
+
command -v jq >/dev/null 2>&1 || { echo '{"supported":false,"error":"jq required"}'; exit 0; }
|
|
38
|
+
|
|
39
|
+
resolve_base_port() {
|
|
40
|
+
local p=""
|
|
41
|
+
if [ -f "$PROJECT_ROOT/.env" ]; then
|
|
42
|
+
p=$(grep -E '^[[:space:]]*HARNESS_BASE_PORT=' "$PROJECT_ROOT/.env" 2>/dev/null | tail -1 | cut -d= -f2 | tr -dc '0-9')
|
|
43
|
+
fi
|
|
44
|
+
if [ -z "$p" ] && [ -f "$PROJECT_ROOT/.harness/config.json" ]; then
|
|
45
|
+
p=$(jq -r '.runtime.ports.base // empty' "$PROJECT_ROOT/.harness/config.json" 2>/dev/null | tr -dc '0-9')
|
|
46
|
+
fi
|
|
47
|
+
echo "$p"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
BASE_PORT="$(resolve_base_port)"
|
|
51
|
+
if [ -n "$BASE_PORT" ]; then KEY="$BASE_PORT"; else KEY="p$(printf '%s' "$PROJECT_ROOT" | cksum | cut -d' ' -f1)"; fi
|
|
52
|
+
LABEL="$GLOBAL_LABEL.$KEY"
|
|
53
|
+
PLIST="$LAUNCH_AGENTS_DIR/$LABEL.plist"
|
|
54
|
+
|
|
55
|
+
supported() { [ "$(uname 2>/dev/null)" = "Darwin" ] && command -v launchctl >/dev/null 2>&1; }
|
|
56
|
+
is_loaded() { launchctl print "$DOMAIN/$1" >/dev/null 2>&1; }
|
|
57
|
+
in_global_list() { [ -f "$PROJECTS_LIST" ] && grep -Fxq "$PROJECT_ROOT" "$PROJECTS_LIST" 2>/dev/null; }
|
|
58
|
+
|
|
59
|
+
# XML-escape values interpolated into the plist (paths may contain & < > " ').
|
|
60
|
+
xml_escape() {
|
|
61
|
+
printf '%s' "$1" | sed -e 's/&/\&/g' -e 's/</\</g' -e 's/>/\>/g' -e 's/"/\"/g' -e "s/'/\'/g"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
write_plist() { # path
|
|
65
|
+
mkdir -p "$(dirname "$1")"
|
|
66
|
+
local e_label e_wake e_out e_err e_path e_root
|
|
67
|
+
e_label="$(xml_escape "$LABEL")"
|
|
68
|
+
e_wake="$(xml_escape "$WAKE_SH")"
|
|
69
|
+
e_out="$(xml_escape "$LOG_DIR/wake.$KEY.stdout.log")"
|
|
70
|
+
e_err="$(xml_escape "$LOG_DIR/wake.$KEY.stderr.log")"
|
|
71
|
+
e_path="$(xml_escape "$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin")"
|
|
72
|
+
e_root="$(xml_escape "$PROJECT_ROOT")"
|
|
73
|
+
cat > "$1" <<PL
|
|
74
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
75
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
76
|
+
<plist version="1.0">
|
|
77
|
+
<dict>
|
|
78
|
+
<key>Label</key><string>$e_label</string>
|
|
79
|
+
<key>ProgramArguments</key>
|
|
80
|
+
<array><string>/bin/bash</string><string>$e_wake</string></array>
|
|
81
|
+
<key>StartInterval</key><integer>3600</integer>
|
|
82
|
+
<key>RunAtLoad</key><true/>
|
|
83
|
+
<key>StandardOutPath</key><string>$e_out</string>
|
|
84
|
+
<key>StandardErrorPath</key><string>$e_err</string>
|
|
85
|
+
<key>EnvironmentVariables</key>
|
|
86
|
+
<dict>
|
|
87
|
+
<key>PATH</key><string>$e_path</string>
|
|
88
|
+
<key>WALWAL_HARNESS_PROJECTS</key><string>$e_root</string>
|
|
89
|
+
</dict>
|
|
90
|
+
</dict>
|
|
91
|
+
</plist>
|
|
92
|
+
PL
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
write_state() { # enabled-bool
|
|
96
|
+
mkdir -p "$STATE_DIR"
|
|
97
|
+
jq -n --arg label "$LABEL" --arg plist "$PLIST" --arg base "$BASE_PORT" --arg key "$KEY" \
|
|
98
|
+
--arg root "$PROJECT_ROOT" --argjson enabled "$1" --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '
|
|
99
|
+
{label:$label, plist:$plist, key:$key,
|
|
100
|
+
base_port:($base | if . == "" then null else (tonumber? // .) end),
|
|
101
|
+
project_root:$root, enabled:$enabled, updated_at:$now}' \
|
|
102
|
+
> "$STATE_DIR/wake-control.json" 2>/dev/null || true
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
status_json() {
|
|
106
|
+
local sup="false" pp="false" gl="false" ing="false" eff="false" plx="false"
|
|
107
|
+
supported && sup="true"
|
|
108
|
+
if supported; then
|
|
109
|
+
is_loaded "$LABEL" && pp="true"
|
|
110
|
+
is_loaded "$GLOBAL_LABEL" && gl="true"
|
|
111
|
+
fi
|
|
112
|
+
in_global_list && ing="true"
|
|
113
|
+
[ -f "$PLIST" ] && plx="true"
|
|
114
|
+
if [ "$pp" = "true" ] || { [ "$gl" = "true" ] && [ "$ing" = "true" ]; }; then eff="true"; fi
|
|
115
|
+
jq -nc \
|
|
116
|
+
--argjson supported "$sup" --argjson enabled "$eff" \
|
|
117
|
+
--argjson per_project_loaded "$pp" --argjson global_loaded "$gl" --argjson in_global_list "$ing" \
|
|
118
|
+
--argjson plist_exists "$plx" \
|
|
119
|
+
--arg label "$LABEL" --arg key "$KEY" --arg plist "$PLIST" --arg root "$PROJECT_ROOT" \
|
|
120
|
+
--arg base "$BASE_PORT" '
|
|
121
|
+
{supported:$supported, enabled:$enabled,
|
|
122
|
+
per_project_loaded:$per_project_loaded, global_loaded:$global_loaded, in_global_list:$in_global_list,
|
|
123
|
+
plist_exists:$plist_exists, label:$label, key:$key, plist:$plist, project_root:$root,
|
|
124
|
+
base_port:($base | if . == "" then null else (tonumber? // .) end),
|
|
125
|
+
interval_seconds:3600}'
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
case "$CMD" in
|
|
129
|
+
label) echo "$LABEL" ;;
|
|
130
|
+
status) status_json ;;
|
|
131
|
+
on)
|
|
132
|
+
if ! supported; then status_json; exit 0; fi
|
|
133
|
+
if [ "$DRYRUN" = "1" ]; then write_plist "$STATE_DIR/$LABEL.plist.preview"; write_state true; status_json; exit 0; fi
|
|
134
|
+
write_plist "$PLIST"
|
|
135
|
+
launchctl bootout "$DOMAIN/$LABEL" >/dev/null 2>&1 || launchctl unload "$PLIST" >/dev/null 2>&1 || true
|
|
136
|
+
launchctl bootstrap "$DOMAIN" "$PLIST" >/dev/null 2>&1 || launchctl load "$PLIST" >/dev/null 2>&1 || true
|
|
137
|
+
# per-project job is authoritative; drop from the legacy global list to avoid double-wake.
|
|
138
|
+
[ -x "$INSTALL_SH" ] && bash "$INSTALL_SH" remove "$PROJECT_ROOT" >/dev/null 2>&1 || true
|
|
139
|
+
write_state true
|
|
140
|
+
status_json
|
|
141
|
+
;;
|
|
142
|
+
off)
|
|
143
|
+
if ! supported; then status_json; exit 0; fi
|
|
144
|
+
launchctl bootout "$DOMAIN/$LABEL" >/dev/null 2>&1 || launchctl unload "$PLIST" >/dev/null 2>&1 || true
|
|
145
|
+
rm -f "$PLIST"
|
|
146
|
+
[ -x "$INSTALL_SH" ] && bash "$INSTALL_SH" remove "$PROJECT_ROOT" >/dev/null 2>&1 || true
|
|
147
|
+
write_state false
|
|
148
|
+
status_json
|
|
149
|
+
;;
|
|
150
|
+
*) echo '{"error":"usage: status|on|off|label <project-root> [--dry-run]"}' >&2; exit 2 ;;
|
|
151
|
+
esac
|
package/scripts/harness-wake.sh
CHANGED
|
@@ -167,6 +167,11 @@ Project root: $project_root
|
|
|
167
167
|
Wake reason: idle=${idle}s force=${force}
|
|
168
168
|
Hourly review: ${review_path:-"(not available)"}
|
|
169
169
|
|
|
170
|
+
Operating (perpetual) goal mode:
|
|
171
|
+
- If the active goal's mission-state.json lifecycle is "operating", this is a never-completing standing company. Do NOT run harness-company-complete.sh for it.
|
|
172
|
+
- Read the shared agenda .harness/documents/<goal>/agenda.json (scripts/harness-agenda.sh . <goal> list). If it has active items, adjudicate every open item (scripts/harness-agenda.sh . <goal> decide <id> "<decision>" <cxx>) and route the owning CXX to execute+verify, then close it. If the agenda is empty, run a status-briefing round: require each CXX (COO/CDO/CTO/CQO/OPS) to confirm its live deliverables still operate toward the goal and to raise any loss/drift/incident/opportunity/risk via scripts/harness-agenda.sh . <goal> raise <cxx> <kind> "<title>".
|
|
173
|
+
- If a full briefing round genuinely surfaces nothing this tick, run scripts/harness-company-cycle.sh . <goal> to record the operating heartbeat and end the tick; the next hourly wake resumes the cycle. A loss/drawdown is an agenda item that triggers the next research→apply→operate cycle, never a reason to stop or to wait on the Owner.
|
|
174
|
+
|
|
170
175
|
One-line command:
|
|
171
176
|
Convene CXX + OPS, collect current progress and decisions, decide the next action as CEO with the active goal in mind, drive all teams without asking Owner, and use CQO verification until the goal demonstrably works.
|
|
172
177
|
|