@walwal-harness/cli 7.1.26 → 7.1.28
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/README.md +8 -1
- package/apps/harness-dashboard/app/globals.css +165 -3
- package/apps/harness-dashboard/app/layout.tsx +1 -5
- package/apps/harness-dashboard/app/page.tsx +3 -7
- package/apps/harness-dashboard/components/Drawer.tsx +7 -7
- package/apps/harness-dashboard/components/MissionTimeline.tsx +4 -4
- package/apps/harness-dashboard/components/OrgTree.tsx +7 -7
- package/apps/harness-dashboard/components/Scene.tsx +1620 -487
- package/apps/harness-dashboard/lib/harness-state.ts +132 -10
- package/apps/harness-dashboard/lib/types.ts +39 -0
- package/apps/harness-dashboard/package-lock.json +1519 -53
- package/apps/harness-dashboard/package.json +2 -0
- package/assets/templates/AGENTS.md.template +8 -4
- package/assets/templates/HARNESS.md +6 -1
- package/assets/templates/todos-state.json.template +12 -0
- package/bin/init.js +43 -0
- package/package.json +1 -1
- package/scripts/harness-user-prompt-submit.sh +42 -0
- package/scripts/lib/harness-structured-log.sh +59 -0
|
@@ -121,6 +121,9 @@ Note: A docmeta skip decision on harness documents (ceo.md, cto.md, cqo.md, work
|
|
|
121
121
|
| `.harness/documents/goal-{index}-{name}/` | Goal-level CXX decisions and worker reports |
|
|
122
122
|
| `.harness/documents/goal-{index}-{name}/submission-{index}-{name}/` | Additional requirement under the active goal |
|
|
123
123
|
| `.harness/documents/goal-{index}-{name}/hotfix-{index}-{name}/` | Emergency fix under the active goal |
|
|
124
|
+
| `.harness/events.jsonl` | Structured append-only runtime event stream |
|
|
125
|
+
| `.harness/todos/state.json` | Machine-readable CXX todo queues |
|
|
126
|
+
| `.harness/todos/events.jsonl` | Structured todo transition history |
|
|
124
127
|
| `.harness/conventions/` | Durable rules (CQO writes, survives missions) |
|
|
125
128
|
| `.harness/gotchas/` | Recurrence-prevention records (CQO writes per hot-fix) |
|
|
126
129
|
| `.harness/memories/` | Long-term shared context |
|
|
@@ -145,10 +148,11 @@ Note: A docmeta skip decision on harness documents (ceo.md, cto.md, cqo.md, work
|
|
|
145
148
|
9. **No verdict without worker evidence** — CQO cannot issue ACCEPTED/REJECTED without a Worker Evidence Manifest referencing at least one evaluator worker. Self-inspection by CQO is not valid evidence.
|
|
146
149
|
10. **Hierarchical worker ownership** — Hired workers are installed under `.claude/skills/{owning-cxx}/{worker}/` and `.codex/skills/{owning-cxx}/{worker}/`; mission worker reports live under `.harness/documents/{goal-or-child-mission}/{owning-cxx}/workers/`. Flat `{mission}/workers/` reports are legacy and signal an ownership violation unless explicitly migrated.
|
|
147
150
|
11. **Lazy convention/gotcha loading** — CXX roles read `.harness/conventions/shared.md`, `.harness/conventions/{cxx}.md`, `.harness/gotchas/shared.md`, and `.harness/gotchas/{cxx}.md`, then follow only the related topic links listed in those CXX files. Workers read only the related links supplied by their owning CXX.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
12. **Implementation Notes required** — `ceo.md`, every `{cxx}.md`, and every worker report must end with one English `## Implementation Notes` section containing `Design Decisions`, `Deviations`, `Tradeoffs`, and `Open Questions`. Use `None` for empty subsections. Do not create a separate sidecar notes file; the notes belong at the bottom of the same role or worker report that produced the decision/evidence.
|
|
152
|
+
13. **Structured runtime state** — If the harness must parse it, record it in JSON or JSONL. CXX todo queues, event history, heartbeat timestamps, preemption/resume state, and completion evidence belong in `.harness/todos/*.json*` or `.harness/events.jsonl`, not in free-form Markdown tables. Markdown remains for instructions, conventions, gotchas, skills, and human-readable mission narrative.
|
|
153
|
+
14. **Owner is final acceptance only** — Owner is not a tester or QA substitute. CEO/CXX must not report "done, please check" until worker-backed verification proves the goal can be completed. Use unit tests, E2E, Playwright, test accounts, seeded data, build/run checks, logs, and CQO evidence before requesting Owner acceptance.
|
|
154
|
+
15. **OPS watches CQO verification** — For runnable products, CQO PASS is invalid unless OPS has monitored the verification runtime or documented why OPS watch is not applicable. Open OPS incidents, missing runtime mapping, required log gaps, service down, health mismatch, or an unmonitored tested service block Owner acceptance.
|
|
155
|
+
16. **Production incidents are company events** — After launch, user-impacting OPS signals route through CEO to CTO/CQO/OPS. CTO owns recovery, CQO owns regression confirmation, and OPS owns evidence plus close criteria.
|
|
152
156
|
|
|
153
157
|
---
|
|
154
158
|
|
|
@@ -51,11 +51,16 @@ Owner is the final acceptance reviewer, not a tester, QA substitute, debugger, o
|
|
|
51
51
|
| `.harness/shared/HR-Resource/` | Hireable worker skill pool |
|
|
52
52
|
| `.harness/shared/hr-roster.json` | Hired worker roster |
|
|
53
53
|
| `.harness/archive/` | CQO-approved completed mission records |
|
|
54
|
-
| `.harness/
|
|
54
|
+
| `.harness/events.jsonl` | Structured append-only runtime event stream |
|
|
55
|
+
| `.harness/todos/state.json` | Machine-readable CXX todo queues |
|
|
56
|
+
| `.harness/todos/events.jsonl` | Structured todo transition history |
|
|
57
|
+
| `.harness/progress.log` | Legacy human-readable command and status history |
|
|
55
58
|
| `.harness/progress.json` | Machine-readable current runtime state |
|
|
56
59
|
|
|
57
60
|
Flat `.harness/documents/{mission}/workers/` reports are legacy. New worker evidence must be under `{owning-cxx}/workers/`.
|
|
58
61
|
|
|
62
|
+
Runtime state that the harness must parse belongs in JSON or JSONL, not Markdown. Markdown remains valid for instructions, skills, conventions, gotchas, and human-facing mission narrative, but queues, events, heartbeats, preemption/resume state, and completion evidence must be machine-readable.
|
|
63
|
+
|
|
59
64
|
## Required Flow
|
|
60
65
|
|
|
61
66
|
### Goal
|
package/bin/init.js
CHANGED
|
@@ -562,6 +562,7 @@ function scaffoldHarness() {
|
|
|
562
562
|
// Copy templates as initial files
|
|
563
563
|
const templateMap = {
|
|
564
564
|
'progress.json.template': path.join(HARNESS_DIR, 'progress.json'),
|
|
565
|
+
'todos-state.json.template': path.join(HARNESS_DIR, 'todos', 'state.json'),
|
|
565
566
|
};
|
|
566
567
|
|
|
567
568
|
const templatesDir = path.join(PKG_ROOT, 'assets', 'templates');
|
|
@@ -569,13 +570,22 @@ function scaffoldHarness() {
|
|
|
569
570
|
for (const [template, dest] of Object.entries(templateMap)) {
|
|
570
571
|
const src = path.join(templatesDir, template);
|
|
571
572
|
if (fs.existsSync(src) && (!fileExists(dest) || isForce)) {
|
|
573
|
+
ensureDir(path.dirname(dest));
|
|
572
574
|
let content = fs.readFileSync(src, 'utf8');
|
|
573
575
|
content = content.replace(/\{\{DATE\}\}/g, new Date().toISOString().split('T')[0]);
|
|
576
|
+
content = content.replace(/\{\{DATE_ISO\}\}/g, new Date().toISOString());
|
|
574
577
|
fs.writeFileSync(dest, content);
|
|
575
578
|
}
|
|
576
579
|
}
|
|
577
580
|
}
|
|
578
581
|
|
|
582
|
+
ensureDir(path.join(HARNESS_DIR, 'todos'));
|
|
583
|
+
for (const rel of ['events.jsonl', path.join('todos', 'events.jsonl')]) {
|
|
584
|
+
const p = path.join(HARNESS_DIR, rel);
|
|
585
|
+
if (!fileExists(p) || isForce) fs.writeFileSync(p, '');
|
|
586
|
+
}
|
|
587
|
+
ensureStructuredRuntimeFiles();
|
|
588
|
+
|
|
579
589
|
// Migrate progress.json v1 → v2 (add mode + team_state fields)
|
|
580
590
|
const progressPath = path.join(HARNESS_DIR, 'progress.json');
|
|
581
591
|
if (fileExists(progressPath)) {
|
|
@@ -711,6 +721,38 @@ function ensureHarnessEnv() {
|
|
|
711
721
|
log('.env: HARNESS_BASE_PORT placeholder added — set value with Owner approval before starting services');
|
|
712
722
|
}
|
|
713
723
|
|
|
724
|
+
function ensureStructuredRuntimeFiles({ dryRun = false } = {}) {
|
|
725
|
+
const todosDir = path.join(HARNESS_DIR, 'todos');
|
|
726
|
+
const statePath = path.join(todosDir, 'state.json');
|
|
727
|
+
const eventsPath = path.join(HARNESS_DIR, 'events.jsonl');
|
|
728
|
+
const todoEventsPath = path.join(todosDir, 'events.jsonl');
|
|
729
|
+
const todoTplPath = path.join(PKG_ROOT, 'assets', 'templates', 'todos-state.json.template');
|
|
730
|
+
|
|
731
|
+
const missing = [eventsPath, statePath, todoEventsPath].filter((p) => !fileExists(p));
|
|
732
|
+
if (!missing.length) return false;
|
|
733
|
+
|
|
734
|
+
log(` structured runtime: ${missing.map((p) => path.relative(HARNESS_DIR, p)).join(', ')} 생성`);
|
|
735
|
+
if (dryRun) return true;
|
|
736
|
+
|
|
737
|
+
ensureDir(todosDir);
|
|
738
|
+
if (!fileExists(eventsPath)) fs.writeFileSync(eventsPath, '');
|
|
739
|
+
if (!fileExists(todoEventsPath)) fs.writeFileSync(todoEventsPath, '');
|
|
740
|
+
if (!fileExists(statePath)) {
|
|
741
|
+
let content;
|
|
742
|
+
if (fileExists(todoTplPath)) {
|
|
743
|
+
content = fs.readFileSync(todoTplPath, 'utf8').replace(/\{\{DATE_ISO\}\}/g, new Date().toISOString());
|
|
744
|
+
} else {
|
|
745
|
+
content = JSON.stringify({
|
|
746
|
+
version: 1,
|
|
747
|
+
updated_at: new Date().toISOString(),
|
|
748
|
+
owners: { ceo: [], coo: [], cdo: [], cto: [], cqo: [], ops: [] },
|
|
749
|
+
}, null, 2) + '\n';
|
|
750
|
+
}
|
|
751
|
+
fs.writeFileSync(statePath, content);
|
|
752
|
+
}
|
|
753
|
+
return true;
|
|
754
|
+
}
|
|
755
|
+
|
|
714
756
|
// ─────────────────────────────────────────
|
|
715
757
|
// 2. Skills → .claude/skills/
|
|
716
758
|
// ─────────────────────────────────────────
|
|
@@ -1742,6 +1784,7 @@ function runMigrate(opts = {}) {
|
|
|
1742
1784
|
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
1743
1785
|
const backupDir = path.join(HARNESS_DIR, 'archive', `migration-${ts}`);
|
|
1744
1786
|
if (!dryRun) ensureDir(backupDir);
|
|
1787
|
+
ensureStructuredRuntimeFiles({ dryRun });
|
|
1745
1788
|
|
|
1746
1789
|
// Runtime scripts are package-owned. Migrate must refresh them so existing
|
|
1747
1790
|
// projects receive wake/meeting/OPS fixes without requiring a full init.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@walwal-harness/cli",
|
|
3
|
-
"version": "7.1.
|
|
3
|
+
"version": "7.1.28",
|
|
4
4
|
"description": "Company-style AI agent harness for Claude and Codex. Installs commands, CXX agents, skills, HR-Resource hiring pool, and project-local .harness runtime state.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"walwal-harness": "bin/init.js"
|
|
@@ -10,6 +10,8 @@ if [ -z "$CWD" ]; then CWD="$PWD"; fi
|
|
|
10
10
|
# 조건 1: 하네스 초기화 확인
|
|
11
11
|
if [ ! -f "$CWD/.harness/config.json" ]; then exit 0; fi
|
|
12
12
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
13
|
+
STRUCTURED_LIB="$SCRIPT_DIR/lib/harness-structured-log.sh"
|
|
14
|
+
[ -f "$STRUCTURED_LIB" ] && source "$STRUCTURED_LIB"
|
|
13
15
|
|
|
14
16
|
# 조건 2: opt-out 플래그 확인
|
|
15
17
|
AUTO_ROUTE="true"
|
|
@@ -71,7 +73,47 @@ if [ -n "$PROMPT" ] && [ -d "$CWD/.harness" ]; then
|
|
|
71
73
|
COMMAND_TYPE="hot-fix"
|
|
72
74
|
fi
|
|
73
75
|
echo "$(date +"%Y-%m-%d %H:%M") | user-prompt | ${COMMAND_TYPE} | ${PROMPT_SHORT}" >> "$PROGRESS_LOG"
|
|
76
|
+
if declare -f harness_emit_event >/dev/null 2>&1; then
|
|
77
|
+
NOW_ISO_EVENT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
78
|
+
EVENT_JSON=$(jq -nc \
|
|
79
|
+
--arg ts "$NOW_ISO_EVENT" \
|
|
80
|
+
--arg command "$COMMAND_TYPE" \
|
|
81
|
+
--arg summary "$PROMPT_SHORT" \
|
|
82
|
+
'{ts:$ts,type:"user_prompt",command:$command,summary:$summary,source:"UserPromptSubmit"}')
|
|
83
|
+
harness_emit_event "$CWD" "$EVENT_JSON"
|
|
84
|
+
fi
|
|
74
85
|
if [ "$COMMAND_TYPE" = "goal" ] || [ "$COMMAND_TYPE" = "submission" ] || [ "$COMMAND_TYPE" = "hot-fix" ]; then
|
|
86
|
+
if declare -f harness_todo_upsert >/dev/null 2>&1; then
|
|
87
|
+
NOW_ISO_TODO=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
88
|
+
PRIORITY=50
|
|
89
|
+
KIND="$COMMAND_TYPE"
|
|
90
|
+
[ "$COMMAND_TYPE" = "hot-fix" ] && PRIORITY=100 && KIND="hotfix"
|
|
91
|
+
TODO_ID="ceo-${COMMAND_TYPE}-$(date -u +%Y%m%dT%H%M%SZ)"
|
|
92
|
+
TODO_JSON=$(jq -nc \
|
|
93
|
+
--arg id "$TODO_ID" \
|
|
94
|
+
--arg owner "ceo" \
|
|
95
|
+
--arg kind "$KIND" \
|
|
96
|
+
--arg title "$PROMPT_SHORT" \
|
|
97
|
+
--arg status "active" \
|
|
98
|
+
--arg now "$NOW_ISO_TODO" \
|
|
99
|
+
--argjson priority "$PRIORITY" \
|
|
100
|
+
'{
|
|
101
|
+
id:$id,
|
|
102
|
+
owner:$owner,
|
|
103
|
+
kind:$kind,
|
|
104
|
+
title:$title,
|
|
105
|
+
status:$status,
|
|
106
|
+
priority:$priority,
|
|
107
|
+
command:$kind,
|
|
108
|
+
mission_path:null,
|
|
109
|
+
required_artifacts:[],
|
|
110
|
+
created_at:$now,
|
|
111
|
+
updated_at:$now,
|
|
112
|
+
last_heartbeat_at:$now,
|
|
113
|
+
blocked_reason:null
|
|
114
|
+
}')
|
|
115
|
+
harness_todo_upsert "$CWD" "$TODO_JSON"
|
|
116
|
+
fi
|
|
75
117
|
if [ -f "$CWD/.harness/progress.json" ] && command -v jq >/dev/null 2>&1; then
|
|
76
118
|
TMP_PROGRESS="${CWD}/.harness/progress.json.tmp.$$"
|
|
77
119
|
NOW_ISO=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Structured runtime helpers for events.jsonl and todos/state.json.
|
|
3
|
+
|
|
4
|
+
harness_json_escape() {
|
|
5
|
+
jq -Rs . 2>/dev/null
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
harness_ensure_structured_runtime() {
|
|
9
|
+
local project_root="${1:-.}"
|
|
10
|
+
local harness_dir="$project_root/.harness"
|
|
11
|
+
[ -d "$harness_dir" ] || return 0
|
|
12
|
+
mkdir -p "$harness_dir/todos"
|
|
13
|
+
[ -f "$harness_dir/events.jsonl" ] || : > "$harness_dir/events.jsonl"
|
|
14
|
+
[ -f "$harness_dir/todos/events.jsonl" ] || : > "$harness_dir/todos/events.jsonl"
|
|
15
|
+
if [ ! -f "$harness_dir/todos/state.json" ]; then
|
|
16
|
+
jq -n --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '{
|
|
17
|
+
version: 1,
|
|
18
|
+
updated_at: $now,
|
|
19
|
+
owners: {ceo: [], coo: [], cdo: [], cto: [], cqo: [], ops: []}
|
|
20
|
+
}' > "$harness_dir/todos/state.json"
|
|
21
|
+
fi
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
harness_emit_event() {
|
|
25
|
+
local project_root="$1"
|
|
26
|
+
local event_json="$2"
|
|
27
|
+
harness_ensure_structured_runtime "$project_root"
|
|
28
|
+
local path="$project_root/.harness/events.jsonl"
|
|
29
|
+
jq -c --arg now "$(date -u +%Y-%m-%dT%H:%M:%SZ)" '
|
|
30
|
+
. + {ts: (.ts // $now)}
|
|
31
|
+
' <<<"$event_json" >> "$path" 2>/dev/null || true
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
harness_todo_upsert() {
|
|
35
|
+
local project_root="$1"
|
|
36
|
+
local todo_json="$2"
|
|
37
|
+
harness_ensure_structured_runtime "$project_root"
|
|
38
|
+
local state="$project_root/.harness/todos/state.json"
|
|
39
|
+
local events="$project_root/.harness/todos/events.jsonl"
|
|
40
|
+
local tmp="$state.tmp.$$"
|
|
41
|
+
local now
|
|
42
|
+
now="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
43
|
+
|
|
44
|
+
jq -c --arg now "$now" '. + {ts: (.ts // $now), type: (.type // "todo_upsert")}' <<<"$todo_json" >> "$events" 2>/dev/null || true
|
|
45
|
+
if jq --argjson todo "$todo_json" --arg now "$now" '
|
|
46
|
+
.version = (.version // 1) |
|
|
47
|
+
.updated_at = $now |
|
|
48
|
+
.owners = (.owners // {ceo: [], coo: [], cdo: [], cto: [], cqo: [], ops: []}) |
|
|
49
|
+
($todo.owner // "ceo") as $owner |
|
|
50
|
+
.owners[$owner] = (.owners[$owner] // []) |
|
|
51
|
+
.owners[$owner] = ((.owners[$owner] | map(select(.id != $todo.id))) + [
|
|
52
|
+
($todo + {updated_at: ($todo.updated_at // $now), created_at: ($todo.created_at // $now)})
|
|
53
|
+
] | sort_by(-(.priority // 0), .created_at))
|
|
54
|
+
' "$state" > "$tmp" 2>/dev/null; then
|
|
55
|
+
mv "$tmp" "$state"
|
|
56
|
+
else
|
|
57
|
+
rm -f "$tmp"
|
|
58
|
+
fi
|
|
59
|
+
}
|