[object Object]

← back to Chief Of Operations

add chief of operations oversight harness

9faff8947468321e9bef48c522e06d507d9e1d6d · 2026-09-17 08:33:09 -0700 · Steve Abrams

Files touched

Diff

commit 9faff8947468321e9bef48c522e06d507d9e1d6d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 17 08:33:09 2026 -0700

    add chief of operations oversight harness
---
 .gitignore   |   6 ++++
 README.md    |  13 ++++++++
 chief_ops.py | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1e68042
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+__pycache__/
+*.pyc
+.env*
+*.log
+.DS_Store
+tmp/
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3d68eb0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+# Chief of Operations
+
+Local read-first harness for keeping the terminal operation on track.
+
+It checks the master `dot-screen-router`, the `desktop-dotbar` launch agent and health endpoint, Plannator (`:9772`), semantic terminal status, router evidence (`green_misplaced` / `attn_misplaced`), screenshot freshness, and archived legacy arrangers.
+
+```sh
+./chief_ops.py check
+./chief_ops.py arrange
+./chief_ops.py serve # http://127.0.0.1:9794
+```
+
+The Arrange action delegates to the existing master router through the desktop bar API. The harness does not delete, publish, or mutate fleet data.
diff --git a/chief_ops.py b/chief_ops.py
new file mode 100755
index 0000000..972ad34
--- /dev/null
+++ b/chief_ops.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+"""Chief of Operations: read-first oversight for layout, dotbar, tickets, and Plannator."""
+from __future__ import annotations
+import argparse, json, os, subprocess, time, urllib.request, urllib.error
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+from pathlib import Path
+
+HOME = Path.home()
+ROUTER = HOME / '.claude/skills/dot-screen-router'
+DOTBAR = HOME / 'Projects/desktop-dotbar'
+STATUS = HOME / '.local/state/abrams-terminal-status/ttys016.json'
+TICKET = 'TK-11882-chief-of-operations-harness-for-terminal'
+SCREENSHOTS = [HOME/'Desktop/arrange-click-screen1.png', HOME/'Desktop/arrange-click-screen2.png']
+ARCHIVE = HOME/'.claude/skills/_disabled-layout-legacy-20260917'
+
+
+def cmd(args, timeout=4):
+    try:
+        p = subprocess.run(args, text=True, capture_output=True, timeout=timeout)
+        return p.returncode, (p.stdout or '').strip(), (p.stderr or '').strip()
+    except Exception as e:
+        return 1, '', str(e)
+
+
+def launchd(label):
+    rc, out, err = cmd(['launchctl','print',f'gui/{os.getuid()}/{label}'])
+    return {'name': label, 'ok': rc == 0 and 'state = running' in out, 'detail': next((x.strip() for x in out.splitlines() if x.strip().startswith('state =')), err or 'not loaded')}
+
+
+def http_health(port, path='/health'):
+    try:
+        with urllib.request.urlopen(f'http://127.0.0.1:{port}{path}', timeout=2) as r:
+            return {'ok': r.status == 200, 'body': json.loads(r.read().decode() or '{}')}
+    except Exception as e:
+        return {'ok': False, 'error': str(e)}
+
+
+def router_evidence():
+    log = ROUTER/'data/router.jsonl'
+    if not log.exists(): return {'ok': False, 'error': 'router evidence missing'}
+    rows=[]
+    for line in log.read_text(errors='replace').splitlines()[-40:]:
+        try: rows.append(json.loads(line))
+        except Exception: pass
+    rows=[r for r in rows if 'green_misplaced' in r]
+    last=rows[-1] if rows else {}
+    return {'ok': bool(rows) and last.get('green_misplaced') == 0 and last.get('attn_misplaced') == 0,
+            'last': last, 'samples': len(rows), 'cellfails': sum(1 for x in log.read_text(errors='replace').splitlines()[-40:] if '"event":"cellfail"' in x)}
+
+
+def check():
+    status={}
+    if STATUS.exists():
+        try:
+            d=json.loads(STATUS.read_text()); status={'ok': d.get('state') == 'green', 'state': d.get('state'), 'title': d.get('title'), 'ticket': d.get('ticket')}
+        except Exception as e: status={'ok':False,'error':str(e)}
+    archived=[p.name for p in (ARCHIVE.iterdir() if ARCHIVE.exists() else [])]
+    screenshots=[]
+    for p in SCREENSHOTS:
+        age_s=(time.time()-p.stat().st_mtime) if p.exists() else None
+        screenshots.append({'path':str(p),'ok':bool(p.exists() and age_s is not None and age_s < 86400),'age_s':round(age_s) if age_s is not None else None})
+    checks={
+      'terminal_status': status,
+      'router_launchd': launchd('com.steve.dot-screen-router'),
+      'dotbar_launchd': launchd('com.steve.desktop-dotbar'),
+      'dotbar_health': http_health(9787),
+      'plannator_health': http_health(9772),
+      'router_evidence': router_evidence(),
+      'legacy_conflicts_archived': {'ok': all(x in archived for x in ('arrange','tile-blue-right','tile-green-left')), 'items': archived},
+      'screenshots': {'ok': all(x['ok'] for x in screenshots), 'items': screenshots},
+    }
+    checks['overall']={'ok': all(v.get('ok',False) for k,v in checks.items() if k not in ('terminal_status',) or v), 'checked_at':time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime())}
+    return checks
+
+
+def arrange():
+    try:
+        req=urllib.request.Request('http://127.0.0.1:9787/api/arrange', method='POST')
+        with urllib.request.urlopen(req, timeout=50) as r: return json.loads(r.read().decode())
+    except Exception as e: return {'ok':False,'error':str(e)}
+
+HTML='''<!doctype html><meta charset="utf-8"><title>Chief of Operations</title><style>body{font:15px -apple-system;background:#101318;color:#e9edf3;margin:32px}h1{margin:0 0 8px}.sub{color:#9aa4b2;margin-bottom:24px}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:12px}.card{background:#1b2029;border:1px solid #303947;border-radius:12px;padding:16px}.ok{color:#54d17a}.bad{color:#ff7676}.muted{color:#9aa4b2;font-size:12px;word-break:break-word}button{background:#315dff;color:white;border:0;border-radius:8px;padding:10px 14px;font-weight:700;cursor:pointer}pre{white-space:pre-wrap;font-size:12px;color:#c9d2df}</style><h1>Chief of Operations</h1><div class="sub">Read-first oversight for the master terminal router, desktop bar, Plannator, and evidence.</div><button onclick="arrange()">Arrange master now</button> <button onclick="load()">Refresh checks</button><div id="out" class="grid" style="margin-top:20px"></div><script>async function load(){let d=await fetch('/api/check').then(r=>r.json());out.innerHTML=Object.entries(d).map(([k,v])=>`<section class="card"><b class="${v.ok?'ok':'bad'}">${v.ok?'●':'●'} ${k}</b><pre>${JSON.stringify(v,null,2)}</pre></section>`).join('')}async function arrange(){let r=await fetch('/api/arrange',{method:'POST'}).then(r=>r.json());alert(JSON.stringify(r));load()}load();setInterval(load,10000)</script>'''
+
+class Handler(BaseHTTPRequestHandler):
+    def log_message(self,*args): pass
+    def do_GET(self):
+        if self.path == '/api/check': self.send_json(check()); return
+        self.send_response(200); self.send_header('Content-Type','text/html'); self.end_headers(); self.wfile.write(HTML.encode())
+    def do_POST(self):
+        if self.path == '/api/arrange': self.send_json(arrange()); return
+        self.send_error(404)
+    def send_json(self, data):
+        b=json.dumps(data, indent=2).encode(); self.send_response(200); self.send_header('Content-Type','application/json'); self.send_header('Content-Length',str(len(b))); self.end_headers(); self.wfile.write(b)
+
+def main():
+    ap=argparse.ArgumentParser(); ap.add_argument('action', choices=['check','arrange','serve'], nargs='?', default='check'); ap.add_argument('--port',type=int,default=9794); a=ap.parse_args()
+    if a.action=='check': print(json.dumps(check(), indent=2)); return
+    if a.action=='arrange': print(json.dumps(arrange(), indent=2)); return
+    print(f'Chief of Operations on http://127.0.0.1:{a.port}', flush=True); ThreadingHTTPServer(('127.0.0.1',a.port), Handler).serve_forever()
+if __name__=='__main__': main()

(oldest)  ·  back to Chief Of Operations  ·  classify sandbox health checks clearly c9fe16d →