@walwal-harness/cli 7.1.40 → 7.1.42

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.
Files changed (84) hide show
  1. package/HR-Resource/cdo/SKILL.md +9 -1
  2. package/HR-Resource/ceo/SKILL.md +33 -2
  3. package/HR-Resource/coo/SKILL.md +9 -1
  4. package/HR-Resource/cqo/SKILL.md +9 -1
  5. package/HR-Resource/cto/SKILL.md +10 -2
  6. package/HR-Resource/ops/SKILL.md +12 -4
  7. package/HR-Resource/ops-prompt-inspector/SKILL.md +11 -6
  8. package/HR-Resource/ops-prompt-inspector/assets/prompt-inspector-static.js +180 -0
  9. package/HR-Resource/ops-prompt-inspector/scripts/setup.py +66 -3
  10. package/apps/harness-dashboard/app/api/metrics/route.ts +17 -0
  11. package/apps/harness-dashboard/app/api/wake/route.ts +41 -0
  12. package/apps/harness-dashboard/app/brick.css +482 -0
  13. package/apps/harness-dashboard/app/layout.tsx +13 -0
  14. package/apps/harness-dashboard/app/page.tsx +2 -2
  15. package/apps/harness-dashboard/components/Scene.tsx +690 -36
  16. package/apps/harness-dashboard/components/brick/BrickDashboard.tsx +149 -0
  17. package/apps/harness-dashboard/components/brick/reports.tsx +291 -0
  18. package/apps/harness-dashboard/components/brick/tweaks.tsx +738 -0
  19. package/apps/harness-dashboard/components/brick/ui.tsx +463 -0
  20. package/apps/harness-dashboard/components/brick/views-graph.tsx +305 -0
  21. package/apps/harness-dashboard/components/brick/views-grid.tsx +543 -0
  22. package/apps/harness-dashboard/components/brick/views-timeline.tsx +238 -0
  23. package/apps/harness-dashboard/lib/__tests__/harness-state.test.ts +1 -202
  24. package/apps/harness-dashboard/lib/__tests__/sandbox-e2e.test.ts +490 -0
  25. package/apps/harness-dashboard/lib/brick/adapter.ts +423 -0
  26. package/apps/harness-dashboard/lib/brick/contract.ts +247 -0
  27. package/apps/harness-dashboard/lib/harness-state.ts +107 -796
  28. package/apps/harness-dashboard/lib/metrics/sampler.ts +545 -0
  29. package/apps/harness-dashboard/lib/types.ts +22 -222
  30. package/apps/harness-dashboard/playwright.config.ts +26 -7
  31. package/assets/templates/AGENTS-ko.md.template +1 -1
  32. package/assets/templates/AGENTS.md.template +12 -2
  33. package/assets/templates/HARNESS.md +2 -0
  34. package/assets/templates/company-pipeline-manifest.json +5 -14
  35. package/assets/templates/worker-report.md.template +38 -0
  36. package/bin/init.js +2 -1
  37. package/commands/goal.md +4 -2
  38. package/commands/hot-fix.md +1 -1
  39. package/commands/submission.md +1 -1
  40. package/package.json +1 -1
  41. package/scripts/conductor-tick.sh +12 -0
  42. package/scripts/harness-agenda.sh +105 -0
  43. package/scripts/harness-company-block.sh +89 -0
  44. package/scripts/harness-company-complete.sh +17 -8
  45. package/scripts/harness-company-cycle.sh +54 -0
  46. package/scripts/harness-progress-set.sh +8 -2
  47. package/scripts/harness-stop.sh +76 -0
  48. package/scripts/harness-wake-control.sh +151 -0
  49. package/scripts/harness-wake.sh +5 -0
  50. package/scripts/harness-worker-dispatch.sh +31 -7
  51. package/apps/harness-dashboard/components/Drawer.tsx +0 -146
  52. package/apps/harness-dashboard/components/Header.tsx +0 -40
  53. package/apps/harness-dashboard/components/MissionTimeline.tsx +0 -214
  54. package/apps/harness-dashboard/components/OrgTree.tsx +0 -459
  55. package/apps/harness-dashboard/components/drawer/AgentLogTab.tsx +0 -56
  56. package/apps/harness-dashboard/components/drawer/DesignPreviewTab.tsx +0 -160
  57. package/apps/harness-dashboard/components/drawer/GotchasTab.tsx +0 -89
  58. package/apps/harness-dashboard/components/drawer/HypothesisTab.tsx +0 -42
  59. package/apps/harness-dashboard/components/drawer/IncidentsTab.tsx +0 -50
  60. package/apps/harness-dashboard/components/drawer/MissionDocTab.tsx +0 -96
  61. package/apps/harness-dashboard/components/drawer/MissionFlowTab.tsx +0 -576
  62. package/apps/harness-dashboard/components/drawer/OwnerHistoryTab.tsx +0 -160
  63. package/apps/harness-dashboard/components/drawer/RoomMetricsTab.tsx +0 -90
  64. package/apps/harness-dashboard/components/drawer/TracksTab.tsx +0 -47
  65. package/apps/harness-dashboard/components/three/ArchiveBoxes.tsx +0 -95
  66. package/apps/harness-dashboard/components/three/DeptBoard.tsx +0 -175
  67. package/apps/harness-dashboard/components/three/Floor3D.tsx +0 -404
  68. package/apps/harness-dashboard/components/three/GoalCard3D.tsx +0 -123
  69. package/apps/harness-dashboard/components/three/HypothesisCards.tsx +0 -71
  70. package/apps/harness-dashboard/components/three/MeetingWhiteboard.tsx +0 -98
  71. package/apps/harness-dashboard/components/three/Minifig3D.tsx +0 -391
  72. package/apps/harness-dashboard/components/three/OpsSiren.tsx +0 -99
  73. package/apps/harness-dashboard/components/three/Stage3D.tsx +0 -104
  74. package/apps/harness-dashboard/components/three/TrackRibbons.tsx +0 -81
  75. package/apps/harness-dashboard/lib/__tests__/i18n.test.ts +0 -16
  76. package/apps/harness-dashboard/lib/__tests__/iso.test.ts +0 -42
  77. package/apps/harness-dashboard/lib/__tests__/state-mapping.test.ts +0 -183
  78. package/apps/harness-dashboard/lib/agent-roster.ts +0 -42
  79. package/apps/harness-dashboard/lib/furniture.ts +0 -165
  80. package/apps/harness-dashboard/lib/i18n.ts +0 -26
  81. package/apps/harness-dashboard/lib/iso.ts +0 -150
  82. package/apps/harness-dashboard/lib/markdown.tsx +0 -306
  83. package/apps/harness-dashboard/lib/path-planning.ts +0 -72
  84. package/apps/harness-dashboard/lib/state-mapping.ts +0 -146
@@ -27,7 +27,15 @@ Before design work, read `.harness/conventions/shared.md`, `.harness/conventions
27
27
 
28
28
  ## Worker Activity Telemetry
29
29
 
30
- Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
30
+ When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="cdo" | .agent_status="running"'`.
31
+
32
+ Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
33
+
34
+ On exit, after writing `cdo.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
35
+
36
+ ## Operating Mode — Status Briefing & Agenda
37
+
38
+ When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), CEO periodically orders a 현황 보고. In it, confirm — with worker-backed evidence — whether your hired workers' live deliverables still operate correctly toward the goal (for CDO: is the shipped UI/UX/brand still consistent and usable?). If you discover a regression, drift, incident, opportunity, or risk, do not silently fix it or sit on it: raise it as an agenda item so CEO can adjudicate and route the next cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise cdo <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). When CEO routes a decided agenda item to you, execute it through hired workers, get CQO verification where behavior must be proven, and report so CEO can close the item.
31
39
 
32
40
  ## Rule
33
41
 
@@ -32,6 +32,37 @@ CEO is authorized to approve routine company operations without Owner confirmati
32
32
 
33
33
  If an operational choice is reversible and uses existing project-local credentials or configuration, CEO decides, records the rationale in `ceo.md`, and continues. Escalate only for new secrets, new spending, legal/business acceptance, unavailable external production access, destructive data action, or a direct conflict with the Owner's stated direction.
34
34
 
35
+ ## Company Loop Termination
36
+
37
+ There are exactly two legitimate ways to end the company loop, and each requires an explicit **runtime transition**. Writing `ceo.md` and `mission-state.json` is not enough: the autonomous Stop loop and the dashboard read `progress.json` runtime state (`conductor.state`, `agent_status`, `current_agent`), not your documents. If you finish a report but never fire the transition, the loop stays `running` and the harness keeps prompting you to continue — this is the "is it done or not?" ambiguity. Avoid it by always ending in one of these two states:
38
+
39
+ 1. **COMPLETE** — the mission is genuinely finished: the final Owner report is in `ceo.md`, all required CXX/worker/OPS evidence is collected, and `mission-state.json` is terminal (`complete`/`closed`/`cancelled`/`superseded`) with `active:false`. As the **literal final action of the turn**, run:
40
+ `bash scripts/harness-company-complete.sh . <reason>`
41
+ 2. **BLOCKED on external authority** — the next action genuinely needs authority the harness cannot infer or obtain (new credentials/secrets, payment approval, legal/business acceptance, unavailable production access, destructive data action, or a direct conflict with the Owner's stated direction). Record the exact missing authority and the internally recommended default in `ceo.md`, set `mission-state.json` lifecycle `blocked` with `active:false`, then run:
42
+ `bash scripts/harness-company-block.sh . "<exact missing authority>"`
43
+
44
+ `Truly done = final Owner report + terminal mission-state.json + the matching runtime transition fired.` Until one of these two transitions runs, the mission is still in progress: keep routing CXX and worker work autonomously between turns. Never fire the completion transition while real CXX/worker/verification work remains, and never end a turn in the `running` state with no further action queued.
45
+
46
+ **Completion applies only to FINITE goals.** A perpetual/operating goal (next section) NEVER completes — `harness-company-complete.sh` must not be run for it.
47
+
48
+ ## Operating (Perpetual) Goals — the never-ending company loop
49
+
50
+ First, **classify the goal**:
51
+
52
+ - **Finite** goal — "build X", "add Y", "fix Z": has a definite done state. Use the Company Loop Termination above.
53
+ - **Operating (perpetual)** goal — "run/operate/monitor/keep growing X", "지속/영구 운영", "make money continuously", anything that should *never* stop (e.g. "build a trading bot and keep it profitable forever"): it must run as a standing company that cycles indefinitely.
54
+
55
+ For an operating goal, set `mission-state.json` to `{"lifecycle":"operating","active":true}` (it stays active forever) and **never** call `harness-company-complete.sh`. The only ways an operating goal ends are: the Owner explicitly orders it stopped (then run `harness-company-complete.sh`), or a true external-authority block (then `harness-company-block.sh`).
56
+
57
+ Run it as an **agenda-driven standing executive loop**. The agenda is the shared meetup file every CXX co-writes at `.harness/documents/{goal}/agenda.json`, managed with `scripts/harness-agenda.sh`. Each operating tick:
58
+
59
+ 1. **Read the agenda** (`scripts/harness-agenda.sh . {goal} list`).
60
+ 2. **If there are active items**, you are *forced* to drive them: for every `open` item, decide and record the decision (`scripts/harness-agenda.sh . {goal} decide <id> "<decision>" <owning-cxx>`), then route that CXX to execute through hired workers and CQO-verify; when an item's work is done and verified, close it (`... close <id>`). Do not end the turn with active agenda.
61
+ 3. **If the agenda is empty ("보고사항 없음")**, run a **status-briefing round**: require each relevant CXX (COO/CDO/CTO/CQO/OPS) to file a 현황 보고 — confirm that its workers' *live deliverables* still operate correctly toward the goal — and to raise any newly discovered loss/drift/opportunity/risk as a new agenda item (`scripts/harness-agenda.sh . {goal} raise <cxx> <kind> "<title>" "<evidence>"`). This is how the company discovers its own next work.
62
+ 4. **If a full briefing round genuinely surfaces nothing**, record the operating heartbeat (`scripts/harness-company-cycle.sh . {goal}`) and end the turn — the hourly wake loop resumes the next cycle. This keeps the company always-on without burning a turn spinning.
63
+
64
+ The canonical operating cycle for a self-improving system: `OPS monitor → on loss/drift/opportunity, COO researches + backtests a new strategy → CTO applies it safely → CQO verifies → OPS operates and watches → (repeat)`. Losses are not failures to report to the Owner; they are agenda items that trigger the next research→apply→operate cycle autonomously.
65
+
35
66
  ## Lazy Rule Loading
36
67
 
37
68
  Before routing or accepting CXX work, enforce lazy loading:
@@ -51,8 +82,8 @@ Before routing or accepting CXX work, enforce lazy loading:
51
82
  - CTO: architecture, platform, API, account, web/app/backend/frontend wiring.
52
83
  - CQO: quality gates, e2e/backtest strategy, regression and archive criteria.
53
84
  - OPS: build/service environment monitoring, CQO verification watch, port map checks, launch observation, production watch, and exception monitoring.
54
- 4. Route completed outputs to the next responsible CXX.
55
- 5. Report outcomes, final acceptance requests, and true external-authority blocks to the Owner.
85
+ 4. Route completed outputs to the next responsible CXX. Keep `progress.json` `current_agent` honest as you route so the dashboard shows the live handoff: each CXX sets `current_agent` to its own role on entry; set it back with `bash scripts/harness-progress-set.sh . '.current_agent="ceo" | .agent_status="running"'` whenever you resume between CXX steps and before the final report.
86
+ 5. Report outcomes, final acceptance requests, and true external-authority blocks to the Owner, then fire the Company Loop Termination transition (complete or blocked).
56
87
 
57
88
  ## Hard Rules
58
89
 
@@ -37,7 +37,15 @@ During planning, COO must determine whether the active runtime exposes MCP serve
37
37
 
38
38
  ## Worker Activity Telemetry
39
39
 
40
- Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
40
+ When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="coo" | .agent_status="running"'`.
41
+
42
+ Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
43
+
44
+ On exit, after writing `coo.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
45
+
46
+ ## Operating Mode — Status Briefing & Agenda
47
+
48
+ When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), CEO periodically orders a 현황 보고. In it, confirm — with worker-backed evidence — whether your hired workers' live deliverables still operate correctly toward the goal (for COO: is the current strategy/plan still valid and performing in research/backtest?). If you discover a loss, drift, regression, incident, opportunity, or risk, do not silently fix it or sit on it: raise it as an agenda item so CEO can adjudicate and route the next cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise coo <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). When CEO routes a decided agenda item to you, execute it through hired workers, get CQO verification where behavior must be proven, and report so CEO can close the item.
41
49
 
42
50
  ## Non-Execution Rule
43
51
 
@@ -26,7 +26,15 @@ Before quality work, read `.harness/conventions/shared.md`, `.harness/convention
26
26
 
27
27
  ## Worker Activity Telemetry
28
28
 
29
- Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
29
+ When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="cqo" | .agent_status="running"'`.
30
+
31
+ Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
32
+
33
+ On exit, after writing `cqo.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
34
+
35
+ ## Operating Mode — Status Briefing & Agenda
36
+
37
+ When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), CEO periodically orders a 현황 보고. In it, confirm — with evaluator-worker evidence — whether the live system still passes the quality/regression bar toward the goal (for CQO: are regression/e2e/perf/security gates still green on the running system?). If you discover a regression, quality drift, incident, or verification gap, do not silently sit on it: raise it as an agenda item so CEO can adjudicate and route the next cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise cqo <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). When CEO routes a decided agenda item to you, run the evaluator/tester workers, issue a verdict, and report so CEO can close the item.
30
38
 
31
39
  ## Hard Rules
32
40
 
@@ -28,13 +28,21 @@ Before engineering work, read `.harness/conventions/shared.md`, `.harness/conven
28
28
 
29
29
  ## Worker Activity Telemetry
30
30
 
31
- Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
31
+ When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="cto" | .agent_status="running"'`.
32
+
33
+ Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
34
+
35
+ On exit, after writing `cto.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
36
+
37
+ ## Operating Mode — Status Briefing & Agenda
38
+
39
+ When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), CEO periodically orders a 현황 보고. In it, confirm — with worker-backed evidence — whether your hired workers' live deliverables still operate correctly toward the goal (for CTO: is the running system stable — errors, latency, uptime, resource limits?). If you discover a failure, drift, incident, opportunity, or risk, do not silently fix it or sit on it: raise it as an agenda item so CEO can adjudicate and route the next cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise cto <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). When CEO routes a decided agenda item to you, implement it through hired workers, get CQO verification, and report so CEO can close the item.
32
40
 
33
41
  ## Browser Automation Briefing
34
42
 
35
43
  Every CTO worker brief that may use Playwright, browser automation, browser-based inspection, E2E, or visual verification must explicitly include this requirement:
36
44
 
37
- > Run Playwright/browser automation with a visible browser. Set `headless: false` in launch/config code, use headed test mode (`--headed`, `PWDEBUG=1`, or equivalent), prefer `channel: 'chrome'` when available, and do not use headless mode unless the Owner has explicitly approved an exception in this mission.
45
+ > Run Playwright/browser automation with a visible browser. Set `headless: false` in launch/config code, use headed test mode (`--headed`, `PWDEBUG=1`, or equivalent), prefer `channel: 'chrome'` when available, and do not use headless mode unless the Owner has explicitly approved an exception in this mission. Pace it middle-fast: `slowMo: 120` (ms) — observable but brisk. Do not use `slowMo: 300`+ (too slow); raise it only if the Owner explicitly asks to slow the demo down.
38
46
 
39
47
  CTO must not accept worker plans or reports that omit this requirement when browser automation is in scope.
40
48
 
@@ -52,13 +52,13 @@ OPS must actively watch the runtime while CQO evaluator workers test the product
52
52
 
53
53
  ## Dev-Mode Prompt Inspector
54
54
 
55
- When OPS builds or runs a React/Next.js UI in development mode, and screen-level discussion would benefit from selecting visible UI elements and mapping intended REST/API behavior, OPS must use the installed `harness-ops-prompt-inspector` skill.
55
+ When OPS builds or runs a React/Next.js or static HTML UI in development mode, and screen-level discussion would benefit from selecting visible UI elements and mapping intended REST/API behavior, OPS must use the installed `harness-ops-prompt-inspector` skill.
56
56
 
57
57
  - Use Prompt Inspector only for development builds or verification environments. Do not inject it into production builds or production service environments.
58
58
  - Install it from the project-local skill path under `.claude/skills/harness-ops-prompt-inspector/` or `.codex/skills/harness-ops-prompt-inspector/`; do not download it at mission runtime.
59
59
  - Run its installer against the target app path before or during the dev-mode run so the toolbar is available in the browser for UI/API binding notes.
60
60
  - Record the install result, target app path, dev server URL, and generated API discovery evidence in OPS environment evidence.
61
- - If the project is not React/Next.js or the build is not development mode, report Prompt Inspector as not applicable instead of forcing installation.
61
+ - If the project is not React/Next.js, static HTML, or the build is not development mode, report Prompt Inspector as not applicable instead of forcing installation.
62
62
 
63
63
  ## Post-Launch Watch
64
64
 
@@ -92,13 +92,21 @@ After service launch, OPS continues the same monitoring duty against `runtime.pr
92
92
 
93
93
  ## Worker Activity Telemetry
94
94
 
95
- Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running.
95
+ When CEO routes this mission to you, set yourself as the live agent on entry so the dashboard shows the handoff: `bash scripts/harness-progress-set.sh . '.current_agent="ops" | .agent_status="running"'`.
96
+
97
+ Before launching any fresh worker session, update `.harness/progress.json` with `scripts/harness-progress-set.sh` so dashboards can show the worker as active. Record the worker name, owning CXX, report path, and `status:"running"` under `company_state.workers`, increment `company_state.active_workers`, and set `conductor.current_action` to `spawn:{worker-name}`. After the worker report is accepted, update that worker to `status:"complete"` and decrement `active_workers`. Do not leave `active_workers:0` while a worker session is running. Require every worker report to open with a `## Status` line whose body is `IN_PROGRESS` while the worker runs and `COMPLETE` once the report is final, so the dashboard shows true worker liveness instead of guessing from file timestamps.
98
+
99
+ On exit, after writing `ops.md` and handing back to CEO, run `bash scripts/harness-progress-set.sh . '.agent_status="completed"'` so the loop advances and the dashboard reflects the finished step. Do not clear `conductor.state`; only the CEO's Company Loop Termination step ends the loop.
100
+
101
+ ## Operating Mode — Status Briefing & Agenda
102
+
103
+ When the active goal is operating (perpetual, `mission-state.json` lifecycle `operating`), OPS is the primary monitor of the never-ending loop. On every CEO 현황 보고, report — with monitoring evidence (logs, health, ports, metrics, P&L or domain KPIs) — whether the live system still operates correctly toward the goal. Any loss, drawdown, drift, incident, degraded health, or opportunity MUST become an agenda item so CEO can adjudicate and route the next research→apply→operate cycle: `bash scripts/harness-agenda.sh . <goal-rel> raise ops <kind> "<title>" "<evidence-path>"` (kinds: loss, drift, incident, opportunity, risk, verification-gap). OPS does not fix systems itself — it surfaces the agenda and supplies evidence; CTO owns the fix, COO owns new-strategy research, CQO owns verification. A clean monitoring round with nothing to raise is itself a valid briefing result.
96
104
 
97
105
  ## Browser Automation Briefing
98
106
 
99
107
  Every OPS worker brief that may use Playwright, browser automation, browser-based monitoring, E2E, or visual/runtime verification must explicitly include this requirement:
100
108
 
101
- > Run Playwright/browser automation with a visible browser. Set `headless: false` in launch/config code, use headed test mode (`--headed`, `PWDEBUG=1`, or equivalent), prefer `channel: 'chrome'` when available, and do not use headless mode unless the Owner has explicitly approved an exception in this mission.
109
+ > Run Playwright/browser automation with a visible browser. Set `headless: false` in launch/config code, use headed test mode (`--headed`, `PWDEBUG=1`, or equivalent), prefer `channel: 'chrome'` when available, and do not use headless mode unless the Owner has explicitly approved an exception in this mission. Pace it middle-fast: `slowMo: 120` (ms) — observable but brisk. Do not use `slowMo: 300`+ (too slow); raise it only if the Owner explicitly asks to slow the demo down.
102
110
 
103
111
  OPS must not accept worker plans or reports that omit this requirement when browser automation is in scope.
104
112
 
@@ -1,12 +1,12 @@
1
1
  ---
2
2
  name: harness-ops-prompt-inspector
3
- description: "OPS-only dev-mode visual API binding for React/Next.js UI elements and REST endpoints."
3
+ description: "OPS-only dev-mode visual API binding for React/Next.js or static HTML UI elements and REST endpoints."
4
4
  permissionMode: bypassPermissions
5
5
  ---
6
6
 
7
7
  # OPS Prompt Inspector
8
8
 
9
- OPS-only visual tool for binding UI elements to REST APIs in React/Next.js projects during development builds.
9
+ OPS-only visual tool for binding UI elements to REST APIs in React/Next.js or static HTML projects during development builds.
10
10
 
11
11
  Use this skill only when OPS is responsible for a dev-mode build/run or verification environment where screen-level communication would be improved by an on-page inspector. Do not install it into production builds.
12
12
 
@@ -29,9 +29,9 @@ bash scripts/install.sh <project_path>
29
29
  ```
30
30
 
31
31
  This automatically:
32
- 1. Detects project type (Next.js App/Pages Router)
33
- 2. Copies component to `components/PromptInspector.tsx`
34
- 3. Injects into root layout (dev mode only)
32
+ 1. Detects project type (Next.js App/Pages Router or static `index.html`)
33
+ 2. Copies component to `components/PromptInspector.tsx`, or static script to `public/prompt-inspector-static.js`
34
+ 3. Injects into root layout or `index.html` (dev mode only)
35
35
  4. Discovers all APIs in the project
36
36
 
37
37
  ## Workflow
@@ -85,7 +85,7 @@ After setup, toolbar appears at bottom-right in dev mode:
85
85
 
86
86
  ## Manual Setup (if auto fails)
87
87
 
88
- 1. Copy `assets/PromptInspector.tsx` to project's `components/`
88
+ 1. For React/Next.js, copy `assets/PromptInspector.tsx` to project's `components/`
89
89
 
90
90
  2. Add to root layout:
91
91
  ```tsx
@@ -95,6 +95,11 @@ import { PromptInspector } from '@/components/PromptInspector';
95
95
  {process.env.NODE_ENV === 'development' && <PromptInspector />}
96
96
  ```
97
97
 
98
+ 3. For static HTML, copy `assets/prompt-inspector-static.js` to `public/` and add before `</body>`:
99
+ ```html
100
+ <script src="/public/prompt-inspector-static.js" data-harness-ops-prompt-inspector></script>
101
+ ```
102
+
98
103
  ## Handler Templates
99
104
 
100
105
  See [references/handler-templates.md](references/handler-templates.md) for code generation patterns.
@@ -0,0 +1,180 @@
1
+ (function () {
2
+ const VERSION = '1.2.0-static';
3
+ const ID = 'harness-ops-prompt-inspector';
4
+ if (window.__HARNESS_OPS_PROMPT_INSPECTOR__) return;
5
+ window.__HARNESS_OPS_PROMPT_INSPECTOR__ = { version: VERSION };
6
+
7
+ const state = {
8
+ selecting: false,
9
+ target: null,
10
+ bindings: [],
11
+ };
12
+
13
+ const css = `
14
+ #${ID}-bar{position:fixed;right:18px;bottom:18px;z-index:2147483647;display:flex;gap:8px;align-items:center;background:#111827;color:#fff;border:1px solid #374151;border-radius:8px;padding:10px;box-shadow:0 16px 36px rgba(0,0,0,.32);font:13px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
15
+ #${ID}-bar button,#${ID}-panel button{border:0;border-radius:6px;padding:8px 10px;background:#2563eb;color:#fff;cursor:pointer;font:inherit}
16
+ #${ID}-bar button.secondary,#${ID}-panel button.secondary{background:#374151}
17
+ #${ID}-bar button.danger{background:#dc2626}
18
+ #${ID}-panel{position:fixed;right:18px;bottom:76px;width:min(420px,calc(100vw - 36px));z-index:2147483647;background:#fff;color:#111827;border:1px solid #d1d5db;border-radius:8px;box-shadow:0 18px 48px rgba(0,0,0,.28);padding:14px;font:13px/1.45 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
19
+ #${ID}-panel h3{margin:0 0 10px;font-size:15px}
20
+ #${ID}-panel label{display:block;margin:10px 0 4px;color:#374151;font-weight:600}
21
+ #${ID}-panel input,#${ID}-panel textarea,#${ID}-panel select{width:100%;box-sizing:border-box;border:1px solid #d1d5db;border-radius:6px;padding:8px;font:inherit}
22
+ #${ID}-panel textarea{min-height:72px;resize:vertical}
23
+ #${ID}-panel .row{display:flex;gap:8px;margin-top:12px}
24
+ #${ID}-highlight{position:fixed;z-index:2147483646;pointer-events:none;border:2px solid #f59e0b;background:rgba(245,158,11,.12);border-radius:4px}
25
+ .${ID}-badge{position:absolute;z-index:2147483645;background:#f59e0b;color:#111827;border-radius:999px;padding:2px 7px;font:11px/1.4 -apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;box-shadow:0 2px 8px rgba(0,0,0,.24)}
26
+ `;
27
+
28
+ const style = document.createElement('style');
29
+ style.textContent = css;
30
+ document.head.appendChild(style);
31
+
32
+ const bar = document.createElement('div');
33
+ bar.id = `${ID}-bar`;
34
+ bar.innerHTML = `
35
+ <strong>Prompt Inspector</strong>
36
+ <button type="button" data-action="select">Select</button>
37
+ <button type="button" class="secondary" data-action="export">Export</button>
38
+ <button type="button" class="danger" data-action="clear">Clear</button>
39
+ `;
40
+ document.body.appendChild(bar);
41
+
42
+ const highlight = document.createElement('div');
43
+ highlight.id = `${ID}-highlight`;
44
+ highlight.hidden = true;
45
+ document.body.appendChild(highlight);
46
+
47
+ function selectorFor(el) {
48
+ if (!el || el === document.body) return 'body';
49
+ if (el.id) return `#${CSS.escape(el.id)}`;
50
+ const parts = [];
51
+ let node = el;
52
+ while (node && node.nodeType === 1 && node !== document.body && parts.length < 4) {
53
+ let part = node.tagName.toLowerCase();
54
+ if (node.classList && node.classList.length) {
55
+ part += `.${Array.from(node.classList).slice(0, 2).map((c) => CSS.escape(c)).join('.')}`;
56
+ }
57
+ const parent = node.parentElement;
58
+ if (parent) {
59
+ const same = Array.from(parent.children).filter((child) => child.tagName === node.tagName);
60
+ if (same.length > 1) part += `:nth-of-type(${same.indexOf(node) + 1})`;
61
+ }
62
+ parts.unshift(part);
63
+ node = parent;
64
+ }
65
+ return parts.join(' > ');
66
+ }
67
+
68
+ function positionHighlight(el) {
69
+ if (!el) {
70
+ highlight.hidden = true;
71
+ return;
72
+ }
73
+ const rect = el.getBoundingClientRect();
74
+ highlight.hidden = false;
75
+ highlight.style.left = `${rect.left}px`;
76
+ highlight.style.top = `${rect.top}px`;
77
+ highlight.style.width = `${rect.width}px`;
78
+ highlight.style.height = `${rect.height}px`;
79
+ }
80
+
81
+ function panelFor(el) {
82
+ const selector = selectorFor(el);
83
+ const panel = document.createElement('div');
84
+ panel.id = `${ID}-panel`;
85
+ panel.innerHTML = `
86
+ <h3>Bind selected element</h3>
87
+ <label>Selector</label>
88
+ <input value="${selector.replace(/"/g, '&quot;')}" data-field="selector">
89
+ <label>Mode</label>
90
+ <select data-field="mode">
91
+ <option value="comment">Comment Only</option>
92
+ <option value="api">Connect API</option>
93
+ </select>
94
+ <label>API / Note</label>
95
+ <input placeholder="GET /api/example or short title" data-field="api">
96
+ <label>Comment</label>
97
+ <textarea placeholder="Describe intended behavior, visual issue, or API binding." data-field="comment"></textarea>
98
+ <div class="row">
99
+ <button type="button" data-action="save">Save</button>
100
+ <button type="button" class="secondary" data-action="cancel">Cancel</button>
101
+ </div>
102
+ `;
103
+ document.body.appendChild(panel);
104
+ panel.querySelector('[data-action="save"]').addEventListener('click', () => {
105
+ const binding = {
106
+ route: `${location.pathname}${location.hash || ''}`,
107
+ selector: panel.querySelector('[data-field="selector"]').value,
108
+ mode: panel.querySelector('[data-field="mode"]').value,
109
+ api: panel.querySelector('[data-field="api"]').value.trim(),
110
+ comment: panel.querySelector('[data-field="comment"]').value.trim(),
111
+ };
112
+ state.bindings.push(binding);
113
+ addBadge(el, state.bindings.length);
114
+ panel.remove();
115
+ state.target = null;
116
+ positionHighlight(null);
117
+ });
118
+ panel.querySelector('[data-action="cancel"]').addEventListener('click', () => {
119
+ panel.remove();
120
+ state.target = null;
121
+ positionHighlight(null);
122
+ });
123
+ }
124
+
125
+ function addBadge(el, index) {
126
+ const rect = el.getBoundingClientRect();
127
+ const badge = document.createElement('div');
128
+ badge.className = `${ID}-badge`;
129
+ badge.textContent = `PI-${index}`;
130
+ badge.style.left = `${Math.max(6, rect.left + window.scrollX)}px`;
131
+ badge.style.top = `${Math.max(6, rect.top + window.scrollY - 20)}px`;
132
+ document.body.appendChild(badge);
133
+ }
134
+
135
+ function exportMarkdown() {
136
+ const lines = [`# Prompt Inspector`, ``, `Route: ${location.pathname}${location.hash || ''}`, ``];
137
+ state.bindings.forEach((binding, i) => {
138
+ lines.push(`## ${i + 1}. ${binding.mode === 'api' ? 'API Binding' : 'Comment'}`);
139
+ lines.push(`- Selector: \`${binding.selector}\``);
140
+ if (binding.api) lines.push(`- API: ${binding.api}`);
141
+ if (binding.comment) lines.push(`- Comment: ${binding.comment}`);
142
+ lines.push('');
143
+ });
144
+ const markdown = lines.join('\n');
145
+ navigator.clipboard?.writeText(markdown).catch(() => {});
146
+ console.log(markdown);
147
+ alert(`Prompt Inspector export copied/logged. Bindings: ${state.bindings.length}`);
148
+ }
149
+
150
+ document.addEventListener('mouseover', (event) => {
151
+ if (!state.selecting || bar.contains(event.target)) return;
152
+ positionHighlight(event.target);
153
+ }, true);
154
+
155
+ document.addEventListener('click', (event) => {
156
+ if (!state.selecting || bar.contains(event.target)) return;
157
+ event.preventDefault();
158
+ event.stopPropagation();
159
+ state.selecting = false;
160
+ state.target = event.target;
161
+ bar.querySelector('[data-action="select"]').textContent = 'Select';
162
+ document.getElementById(`${ID}-panel`)?.remove();
163
+ panelFor(event.target);
164
+ }, true);
165
+
166
+ bar.addEventListener('click', (event) => {
167
+ const action = event.target && event.target.getAttribute('data-action');
168
+ if (action === 'select') {
169
+ state.selecting = !state.selecting;
170
+ event.target.textContent = state.selecting ? 'Selecting...' : 'Select';
171
+ if (!state.selecting) positionHighlight(null);
172
+ }
173
+ if (action === 'export') exportMarkdown();
174
+ if (action === 'clear') {
175
+ state.bindings = [];
176
+ document.querySelectorAll(`.${ID}-badge`).forEach((el) => el.remove());
177
+ positionHighlight(null);
178
+ }
179
+ });
180
+ })();
@@ -34,6 +34,9 @@ def find_project_type(project_path: Path) -> dict:
34
34
  'components_dir': None,
35
35
  }
36
36
 
37
+ # Check for static/vanilla apps
38
+ static_index = project_path / 'index.html'
39
+
37
40
  # Check for Next.js App Router
38
41
  app_layout = project_path / 'app' / 'layout.tsx'
39
42
  app_layout_js = project_path / 'app' / 'layout.js'
@@ -81,6 +84,10 @@ def find_project_type(project_path: Path) -> dict:
81
84
  result['type'] = 'nextjs-pages'
82
85
  result['layout_file'] = src_pages_app_js
83
86
  result['components_dir'] = project_path / 'src' / 'components'
87
+ elif static_index.exists():
88
+ result['type'] = 'static-html'
89
+ result['layout_file'] = static_index
90
+ result['components_dir'] = project_path / 'public'
84
91
 
85
92
  return result
86
93
 
@@ -227,6 +234,46 @@ def inject_into_layout(layout_file: Path, project_type: str) -> bool:
227
234
  return True
228
235
 
229
236
 
237
+ def copy_static_inspector(public_dir: Path, script_dir: Path, force_update: bool = False) -> bool:
238
+ """Copy the static Prompt Inspector browser script."""
239
+ public_dir.mkdir(parents=True, exist_ok=True)
240
+ source = script_dir.parent / 'assets' / 'prompt-inspector-static.js'
241
+ dest = public_dir / 'prompt-inspector-static.js'
242
+ if not source.exists():
243
+ print(f" Error: Source static inspector not found at {source}")
244
+ return False
245
+ if dest.exists() and not force_update:
246
+ print(f" Static inspector already exists at {dest}")
247
+ return True
248
+ shutil.copy(source, dest)
249
+ print(f" Installed static Prompt Inspector at {dest}")
250
+ return True
251
+
252
+
253
+ def inject_into_static_html(index_file: Path) -> bool:
254
+ """Inject the static inspector script into index.html."""
255
+ content = index_file.read_text(encoding='utf-8')
256
+ marker = 'data-harness-ops-prompt-inspector'
257
+ if marker in content:
258
+ print(f" Static inspector already injected in {index_file}")
259
+ return True
260
+
261
+ script_tag = ' <script src="/public/prompt-inspector-static.js" data-harness-ops-prompt-inspector></script>'
262
+ if '</body>' in content:
263
+ new_content = content.replace('</body>', f'{script_tag}\n</body>')
264
+ else:
265
+ new_content = content + f'\n{script_tag}\n'
266
+
267
+ backup_file = index_file.with_suffix(index_file.suffix + '.backup')
268
+ if not backup_file.exists():
269
+ shutil.copy(index_file, backup_file)
270
+ print(f" Backup created at {backup_file}")
271
+
272
+ index_file.write_text(new_content, encoding='utf-8')
273
+ print(f" Injected static inspector into {index_file}")
274
+ return True
275
+
276
+
230
277
  def main():
231
278
  if len(sys.argv) < 2:
232
279
  print("Usage: python setup.py <project_path> [options]")
@@ -266,9 +313,9 @@ def main():
266
313
 
267
314
  if not project_info['type']:
268
315
  print("Could not detect project type.")
269
- print("Supported: Next.js (App Router / Pages Router)")
270
- print("\nManual setup required. Copy PromptInspector.tsx to your components folder")
271
- print("and import it in your root layout/app file.")
316
+ print("Supported: Next.js (App Router / Pages Router), static index.html")
317
+ print("\nManual setup required. Copy PromptInspector.tsx or prompt-inspector-static.js")
318
+ print("and import it in your root layout/app file or index.html.")
272
319
  return 1
273
320
 
274
321
  print(f"Detected: {project_info['type']}")
@@ -277,6 +324,12 @@ def main():
277
324
 
278
325
  # Check-only mode
279
326
  if check_only:
327
+ if project_info['type'] == 'static-html':
328
+ dest = project_info['components_dir'] / 'prompt-inspector-static.js'
329
+ print(f"\n Static install check:")
330
+ print(f" └─ Installed: {'yes' if dest.exists() else 'no'}")
331
+ return 0 if dest.exists() else 1
332
+
280
333
  source = script_dir.parent / 'assets' / 'PromptInspector.tsx'
281
334
  dest = project_info['components_dir'] / 'PromptInspector.tsx'
282
335
 
@@ -299,6 +352,16 @@ def main():
299
352
 
300
353
  print("\nSetting up Prompt Inspector...")
301
354
 
355
+ if project_info['type'] == 'static-html':
356
+ if not copy_static_inspector(project_info['components_dir'], script_dir, force_update):
357
+ return 1
358
+ if not inject_into_static_html(project_info['layout_file']):
359
+ return 1
360
+ print("\nSetup complete!")
361
+ print("Run your dev server and look for the toolbar in the bottom-right corner.")
362
+ print("The static inspector is intended for development servers only.")
363
+ return 0
364
+
302
365
  # Step 1: Copy component
303
366
  if not copy_component(project_info['components_dir'], script_dir, force_update):
304
367
  return 1
@@ -0,0 +1,17 @@
1
+ import { NextResponse } from "next/server";
2
+ import { sampleMetrics } from "@/lib/metrics/sampler";
3
+ import { resolveHarnessRoot } from "@/lib/harness-root";
4
+
5
+ // node:fs + ps/tmux child_process require the Node.js runtime (not Edge).
6
+ export const runtime = "nodejs";
7
+ export const dynamic = "force-dynamic";
8
+ export const revalidate = 0;
9
+
10
+ export async function GET() {
11
+ const root = resolveHarnessRoot();
12
+ const sample = await sampleMetrics(root);
13
+ return NextResponse.json(sample, {
14
+ status: 200,
15
+ headers: { "Cache-Control": "no-store" },
16
+ });
17
+ }
@@ -0,0 +1,41 @@
1
+ import { NextResponse } from "next/server";
2
+ import { execFileSync } from "node:child_process";
3
+ import { existsSync } from "node:fs";
4
+ import path from "node:path";
5
+ import { resolveHarnessRoot } from "@/lib/harness-root";
6
+
7
+ export const dynamic = "force-dynamic";
8
+
9
+ // Per-project hourly-wake launchd control, keyed by HARNESS_BASE_PORT. The
10
+ // dashboard runs locally for the Owner, so it may toggle the Owner's own
11
+ // launchd job. Only the fixed subcommands status|on|off are ever invoked.
12
+ function runControl(action: "status" | "on" | "off") {
13
+ const root = resolveHarnessRoot();
14
+ const script = path.join(root, "scripts", "harness-wake-control.sh");
15
+ if (!existsSync(script)) {
16
+ return { supported: false, enabled: false, error: "harness-wake-control.sh not installed in this project" };
17
+ }
18
+ try {
19
+ const out = execFileSync("bash", [script, action, root], {
20
+ encoding: "utf8",
21
+ timeout: 20000,
22
+ });
23
+ const lastLine = out.trim().split("\n").filter(Boolean).pop() ?? "{}";
24
+ return JSON.parse(lastLine);
25
+ } catch (e) {
26
+ return { supported: false, enabled: false, error: (e as Error).message };
27
+ }
28
+ }
29
+
30
+ export async function GET() {
31
+ return NextResponse.json(runControl("status"));
32
+ }
33
+
34
+ export async function POST(req: Request) {
35
+ const body = (await req.json().catch(() => ({}))) as { action?: string };
36
+ const action = body.action === "on" ? "on" : body.action === "off" ? "off" : null;
37
+ if (!action) {
38
+ return NextResponse.json({ error: "action must be 'on' or 'off'" }, { status: 400 });
39
+ }
40
+ return NextResponse.json(runControl(action));
41
+ }