[object Object]

← back to Chief Of Operations

add continuous DW-priority ticket supervision

dd5f1884faf3fed801e1379be12192233b0d7392 · 2026-09-17 08:58:17 -0700 · Steve Abrams

Files touched

Diff

commit dd5f1884faf3fed801e1379be12192233b0d7392
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 17 08:58:17 2026 -0700

    add continuous DW-priority ticket supervision
---
 README.md          |  8 ++++++++
 chief_ops.py       | 32 +++++++++++++++++++++++++++++++-
 supervisor_loop.sh |  8 ++++++++
 3 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 10c570e..8e2b9fe 100644
--- a/README.md
+++ b/README.md
@@ -11,3 +11,11 @@ It checks the master `dot-screen-router`, the `desktop-dotbar` launch agent and
 ```
 
 The Arrange action delegates to the existing master router through the desktop bar API. The harness does not delete, publish, or mutate fleet data.
+
+## Continuous ticket supervision
+
+`supervisor_loop.sh` refreshes `state/queue.json` every 60 seconds under launchd (`com.steve.chief-of-operations`). The queue includes open, blocked, and doing tickets, scores Designer Wallcoverings/client-impact work first, and records the next safe step. It does not auto-claim tickets or bypass approval gates.
+
+```sh
+./chief_ops.py queue
+```
diff --git a/chief_ops.py b/chief_ops.py
index 723ca22..3bf4681 100755
--- a/chief_ops.py
+++ b/chief_ops.py
@@ -8,6 +8,8 @@ from pathlib import Path
 HOME = Path.home()
 ROUTER = HOME / '.claude/skills/dot-screen-router'
 DOTBAR = HOME / 'Projects/desktop-dotbar'
+TICKET_LIB = HOME / 'Projects/ticket-system/lib.js'
+STATE = Path(__file__).with_name('state')
 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']
@@ -74,6 +76,31 @@ def plannator_state():
         except Exception: continue
     return {'ok': root.exists(), 'manifest_count': len(files), 'active': active[-20:], 'blocked': blocked[-20:]}
 
+def ticket_queue(write=False):
+    js = "const l=require(process.argv[1]); console.log(JSON.stringify([...l.tickets().values()].map(t=>({id:t.id,title:t.title,project:t.project,status:t.status,updated_at:t.updated_at,assignee:t.assignee||''}))));"
+    rc,out,err=cmd(['node','-e',js,str(TICKET_LIB)], timeout=12)
+    if rc or not out: return {'ok':False,'error':err or 'ticket ledger unavailable','items':[]}
+    try: rows=json.loads(out)
+    except Exception as e: return {'ok':False,'error':str(e),'items':[]}
+    dw_terms=('designer wallcoverings','designerwallcoverings','dw-commerce','dw-catalog','dw_unified','shopify','wallcovering','wallpaper','vendor','microsite','catalog')
+    client_terms=('client','customer','live','production','revenue','merchant','shopify','storefront')
+    items=[]
+    for t in rows:
+        if t.get('status') not in ('open','blocked','doing'): continue
+        text=(str(t.get('title',''))+' '+str(t.get('project',''))).lower()
+        dw=any(x in text for x in dw_terms); client=any(x in text for x in client_terms)
+        status_score={'blocked':70,'open':60,'doing':35}.get(t.get('status'),0)
+        score=status_score + (100 if dw else 0) + (35 if client else 0)
+        if t.get('status')=='blocked': next_step='review blocker and route the next reversible step'
+        elif t.get('status')=='open': next_step='claim or dispatch through Plannator'
+        else: next_step='verify active work and unblock dependencies'
+        items.append({**t,'score':score,'dw_priority':dw,'client_impact':client,'next_step':next_step})
+    items.sort(key=lambda x:(-x['score'], x.get('updated_at') or ''))
+    result={'ok':True,'generated_at':time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime()),'total':len(items),'dw_count':sum(x['dw_priority'] for x in items),'blocked_count':sum(x['status']=='blocked' for x in items),'items':items[:40]}
+    if write:
+        STATE.mkdir(exist_ok=True); (STATE/'queue.json').write_text(json.dumps(result,indent=2)+'\n')
+    return result
+
 
 def check():
     status={}
@@ -93,6 +120,7 @@ def check():
       'dotbar_health': http_health(9787),
       'plannator_health': http_health(9772),
       'plannator_state': plannator_state(),
+      'ticket_queue': ticket_queue(write=True),
       '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},
@@ -114,6 +142,7 @@ class Handler(BaseHTTPRequestHandler):
     def log_message(self,*args): pass
     def do_GET(self):
         if self.path == '/api/check': self.send_json(check()); return
+        if self.path == '/api/queue': self.send_json(ticket_queue(write=True)); 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
@@ -122,8 +151,9 @@ class Handler(BaseHTTPRequestHandler):
         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=9896); a=ap.parse_args()
+    ap=argparse.ArgumentParser(); ap.add_argument('action', choices=['check','arrange','queue','serve'], nargs='?', default='check'); ap.add_argument('--port',type=int,default=9896); ap.add_argument('--write',action='store_true'); 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
+    if a.action=='queue': print(json.dumps(ticket_queue(write=a.write or True), 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()
diff --git a/supervisor_loop.sh b/supervisor_loop.sh
new file mode 100755
index 0000000..6060911
--- /dev/null
+++ b/supervisor_loop.sh
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+set -u
+cd "$(dirname "$0")"
+INTERVAL="${CHIEF_OPS_INTERVAL:-60}"
+while true; do
+  ./chief_ops.py queue --write >> state/supervisor.log 2>&1 || true
+  sleep "$INTERVAL"
+done

← 9614de0 classify authenticated health endpoints  ·  back to Chief Of Operations  ·  report supervisor launchd health 030f843 →