cleanup: P4 worker code quality fixes

- Extract renderStatusTable() from nested proc in status()
- Add column width constants (ColTask, ColState, etc.)
- Use parseEnum for heartbeat status instead of case statement
- Return bool from startGlobalHeartbeat() to indicate if started
- Remove unused getBranchStatus() function (dead code)

Closes: skills-y76g, skills-f8yd, skills-audh, skills-hx1n, skills-827e

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
dan 2026-01-11 15:46:34 -08:00
parent c5fd9a6be2
commit b0719d2c59
3 changed files with 94 additions and 94 deletions

View file

@ -56,87 +56,100 @@ proc spawn(taskId: string, description: string = "",
echo " State: ASSIGNED"
echo " Review: (review-gate not available)"
# Status table column widths
const
ColTask = 14
ColState = 12
ColReview = 10
ColAge = 8
ColHeartbeat = 12
ColStatus = 8
ColSummaryMax = 30
proc renderStatusTable(stateFilter: string, staleOnly: bool, asJson: bool) =
## Render worker status as table or JSON
let db = openBusDb()
defer: db.close()
let workers = db.getAllWorkers()
if asJson:
var arr = newJArray()
for w in workers:
let rs = getReviewState(w.taskId)
arr.add(%*{
"task_id": w.taskId,
"state": $w.state,
"branch": w.branch,
"stale": w.staleLevel,
"age": $(getTime() - w.createdAt),
"review": $rs.status
})
echo arr.pretty
return
# Filter
var filtered = workers
if stateFilter != "":
filtered = workers.filterIt($it.state == stateFilter.toUpperAscii)
if staleOnly:
filtered = filtered.filterIt(it.isStale)
if filtered.len == 0:
echo "No workers found."
return
# Table header
echo ""
echo "TASK".alignLeft(ColTask), "STATE".alignLeft(ColState),
"REVIEW".alignLeft(ColReview), "AGE".alignLeft(ColAge),
"HEARTBEAT".alignLeft(ColHeartbeat), "STATUS".alignLeft(ColStatus), "SUMMARY"
echo "-".repeat(80)
for w in filtered:
let age = getTime() - w.createdAt
let ageStr = if age.inHours > 0: &"{age.inHours}h"
elif age.inMinutes > 0: &"{age.inMinutes}m"
else: &"{age.inSeconds}s"
var hbStr = "--"
if w.lastHeartbeat != Time():
let hbAge = getTime() - w.lastHeartbeat
hbStr = if hbAge.inMinutes > 0: &"{hbAge.inMinutes}m ago"
else: &"{hbAge.inSeconds}s ago"
let rs = getReviewState(w.taskId)
let reviewStr = case rs.status
of rsNone: "--"
of rsPending: "pending"
of rsApproved: "approved"
of rsRejected: "REJECTED"
let staleStr = w.staleLevel
let summary = if w.description.len > ColSummaryMax:
w.description[0..<ColSummaryMax] & "..."
else: w.description
echo w.taskId.alignLeft(ColTask),
($w.state).alignLeft(ColState),
reviewStr.alignLeft(ColReview),
ageStr.alignLeft(ColAge),
hbStr.alignLeft(ColHeartbeat),
staleStr.alignLeft(ColStatus),
summary
proc status(state: string = "", stale: bool = false,
json: bool = false, watch: bool = false) =
## Show dashboard of all workers
proc render() =
let db = openBusDb()
defer: db.close()
let workers = db.getAllWorkers()
if json:
var arr = newJArray()
for w in workers:
let rs = getReviewState(w.taskId)
arr.add(%*{
"task_id": w.taskId,
"state": $w.state,
"branch": w.branch,
"stale": w.staleLevel,
"age": $(getTime() - w.createdAt),
"review": $rs.status
})
echo arr.pretty
return
# Filter
var filtered = workers
if state != "":
filtered = workers.filterIt($it.state == state.toUpperAscii)
if stale:
filtered = filtered.filterIt(it.isStale)
if filtered.len == 0:
echo "No workers found."
return
# Table header
echo ""
echo "TASK".alignLeft(14), "STATE".alignLeft(12), "REVIEW".alignLeft(10),
"AGE".alignLeft(8), "HEARTBEAT".alignLeft(12), "STATUS".alignLeft(8), "SUMMARY"
echo "-".repeat(80)
for w in filtered:
let age = getTime() - w.createdAt
let ageStr = if age.inHours > 0: &"{age.inHours}h"
elif age.inMinutes > 0: &"{age.inMinutes}m"
else: &"{age.inSeconds}s"
var hbStr = "--"
if w.lastHeartbeat != Time():
let hbAge = getTime() - w.lastHeartbeat
hbStr = if hbAge.inMinutes > 0: &"{hbAge.inMinutes}m ago"
else: &"{hbAge.inSeconds}s ago"
let rs = getReviewState(w.taskId)
let reviewStr = case rs.status
of rsNone: "--"
of rsPending: "pending"
of rsApproved: "approved"
of rsRejected: "REJECTED"
let staleStr = w.staleLevel
let summary = if w.description.len > 30: w.description[0..29] & "..."
else: w.description
echo w.taskId.alignLeft(14),
($w.state).alignLeft(12),
reviewStr.alignLeft(10),
ageStr.alignLeft(8),
hbStr.alignLeft(12),
staleStr.alignLeft(8),
summary
if watch:
while true:
eraseScreen()
setCursorPos(0, 0)
echo "Worker Status (", now().format("HH:mm:ss"), ") - Press Ctrl+C to exit"
render()
renderStatusTable(state, stale, json)
sleep(2000)
else:
render()
renderStatusTable(state, stale, json)
proc approve(taskId: string) =
## Approve completed work (IN_REVIEW → APPROVED)
@ -254,7 +267,7 @@ proc start(task: string = "") =
quit(ExitSuccess)
# Start heartbeat before transition so we're heartbeating when state changes
startGlobalHeartbeat(dbPath, taskId)
discard startGlobalHeartbeat(dbPath, taskId)
try:
db.transition(taskId, wsAssigned, wsWorking)
@ -348,11 +361,10 @@ proc sendHeartbeat(status: string = "working", progress: float = 0.0) =
let db = openBusDb(getMainRepoBusDbPath())
defer: db.close()
let hs = case status
of "idle": hsIdle
of "working": hsWorking
of "blocked": hsBlocked
else: hsWorking
let hs = try:
parseEnum[HeartbeatStatus](status)
except ValueError:
hsWorking # Default for unknown status
db.writeHeartbeat(ctx.taskId, hs, ctx.taskId, progress)
echo "Heartbeat: ", ctx.taskId, " - ", status

View file

@ -138,17 +138,3 @@ proc getConflictedFiles*(worktree: string): seq[string] =
if line.strip() != "":
result.add(line.strip())
proc getBranchStatus*(worktree: string): tuple[ahead, behind: int] =
## Get commits ahead/behind integration
let (output, code) = runGit(["rev-list", "--left-right", "--count",
"origin/integration...HEAD"], workDir = worktree)
if code != 0:
return (0, 0)
let parts = output.strip().split('\t')
if parts.len >= 2:
try:
result.behind = parseInt(parts[0])
result.ahead = parseInt(parts[1])
except ValueError as e:
logWarn("getBranchStatus", "failed to parse rev-list output '" & output.strip() & "': " & e.msg)

View file

@ -107,11 +107,13 @@ proc stopHeartbeat*(hb: HeartbeatThread) =
# Simpler API using global state (for single-threaded CLI usage)
var globalHeartbeat: HeartbeatThread = nil
proc startGlobalHeartbeat*(dbPath, agentId: string) =
## Start heartbeat using global state (simpler API for CLI)
proc startGlobalHeartbeat*(dbPath, agentId: string): bool =
## Start heartbeat using global state (simpler API for CLI).
## Returns true if started, false if already running.
if globalHeartbeat != nil:
return # Already running
return false # Already running
globalHeartbeat = startHeartbeat(dbPath, agentId)
return true
proc updateGlobalHeartbeat*(status: HeartbeatStatus, task: string = "",
progress: float = 0.0) =