diff --git a/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/.openspec.yaml b/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/.openspec.yaml new file mode 100644 index 0000000..9f70866 --- /dev/null +++ b/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-15 diff --git a/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/proposal.md b/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/proposal.md new file mode 100644 index 0000000..313eca3 --- /dev/null +++ b/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/proposal.md @@ -0,0 +1,11 @@ +## Why + +- TODO: describe the user/problem outcome this change addresses. + +## What Changes + +- TODO: summarize the intended behavior and scope. + +## Impact + +- TODO: call out risks, rollout notes, and affected surfaces. diff --git a/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/specs/tui-fleet-pane-health/spec.md b/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/specs/tui-fleet-pane-health/spec.md new file mode 100644 index 0000000..17f2541 --- /dev/null +++ b/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/specs/tui-fleet-pane-health/spec.md @@ -0,0 +1,9 @@ +## ADDED Requirements + +### Requirement: tui-fleet-pane-health behavior +The system SHALL enforce tui-fleet-pane-health behavior as defined by this change. + +#### Scenario: Baseline acceptance +- **WHEN** tui-fleet-pane-health behavior is exercised +- **THEN** the expected outcome is produced +- **AND** regressions are covered by tests. diff --git a/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/tasks.md b/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/tasks.md new file mode 100644 index 0000000..6946d07 --- /dev/null +++ b/openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/tasks.md @@ -0,0 +1,34 @@ +## Definition of Done + +This change is complete only when **all** of the following are true: + +- Every checkbox below is checked. +- The agent branch reaches `MERGED` state on `origin` and the PR URL + state are recorded in the completion handoff. +- If any step blocks (test failure, conflict, ambiguous result), append a `BLOCKED:` line under section 4 explaining the blocker and **STOP**. Do not tick remaining cleanup boxes; do not silently skip the cleanup pipeline. + +## Handoff + +- Handoff: change=`agent-claude-tui-fleet-pane-health-2026-05-15-23-39`; branch=`agent//`; scope=`TODO`; action=`continue this sandbox or finish cleanup after a usage-limit/manual takeover`. +- Copy prompt: Continue `agent-claude-tui-fleet-pane-health-2026-05-15-23-39` on branch `agent//`. Work inside the existing sandbox, review `openspec/changes/agent-claude-tui-fleet-pane-health-2026-05-15-23-39/tasks.md`, continue from the current state instead of creating a new sandbox, and when the work is done run `gx branch finish --branch agent// --base dev --via-pr --wait-for-merge --cleanup`. + +## 1. Specification + +- [ ] 1.1 Finalize proposal scope and acceptance criteria for `agent-claude-tui-fleet-pane-health-2026-05-15-23-39`. +- [ ] 1.2 Define normative requirements in `specs/tui-fleet-pane-health/spec.md`. + +## 2. Implementation + +- [ ] 2.1 Implement scoped behavior changes. +- [ ] 2.2 Add/update focused regression coverage. + +## 3. Verification + +- [ ] 3.1 Run targeted project verification commands. +- [ ] 3.2 Run `openspec validate agent-claude-tui-fleet-pane-health-2026-05-15-23-39 --type change --strict`. +- [ ] 3.3 Run `openspec validate --specs`. + +## 4. Cleanup (mandatory; run before claiming completion) + +- [ ] 4.1 Run the cleanup pipeline: `gx branch finish --branch agent// --base dev --via-pr --wait-for-merge --cleanup`. This handles commit -> push -> PR create -> merge wait -> worktree prune in one invocation. +- [ ] 4.2 Record the PR URL and final merge state (`MERGED`) in the completion handoff. +- [ ] 4.3 Confirm the sandbox worktree is gone (`git worktree list` no longer shows the agent path; `git branch -a` shows no surviving local/remote refs for the branch). diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/README.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/README.md new file mode 100644 index 0000000..4f78b72 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/README.md @@ -0,0 +1,30 @@ +# Plan Workspace: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 + +This folder stores durable planning artifacts before implementation changes. + +## Shared files +- `summary.md` +- `checkpoints.md` +- `phases.md` +- `open-questions.md` +- `coordinator-prompt.md` +- `kickoff-prompts.md` + +## Role folders +- `planner/` +- `architect/` +- `critic/` +- `executor/` +- `writer/` +- `verifier/` + +When Codex or Claude hits an unresolved question that should survive chat, add it to `open-questions.md` as an unchecked `- [ ]` item. + +Each role folder contains OpenSpec-style artifacts: +- `.openspec.yaml` +- `prompt.md` (copy/paste role prompt) +- `proposal.md` +- `tasks.md` (Spec / Tests / Implementation / Checkpoints checklists) +- `specs//spec.md` +Planner also gets `plan.md`; executor also gets `checkpoints.md`. +Planner plans should follow `openspec/plan/PLANS.md`. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/.openspec.yaml b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/.openspec.yaml new file mode 100644 index 0000000..9d29451 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 +role: architect +status: draft +artifacts: + prompt: prompt.md + proposal: proposal.md + tasks: tasks.md + spec: specs/architect/spec.md diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/README.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/README.md new file mode 100644 index 0000000..1e1c275 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/README.md @@ -0,0 +1,12 @@ +# architect + +Role workspace for `architect`. + +Default artifacts: +- `.openspec.yaml` +- `prompt.md` +- `proposal.md` +- `tasks.md` +- `specs//spec.md` + +Use this folder for role notes, artifacts, and status updates. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/prompt.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/prompt.md new file mode 100644 index 0000000..5634133 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/prompt.md @@ -0,0 +1,34 @@ +# architect Prompt + +You are the `architect` role for OpenSpec plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/tasks.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/proposal.md` + +## Before edits + +1. Confirm branch/worktree with `git status --short --branch`. +2. Claim every touched file before editing: + - Prefer Colony `task_claim_file` when an active task exists. + - Otherwise run `gx locks claim --branch `. +3. Stay inside assigned files/modules; coordinate before touching shared paths. + +## Working rules + +- Update `architect/tasks.md` as each item completes. +- Record durable unresolved questions in `open-questions.md`. +- Keep handoffs short: files changed, behavior touched, verification, risks. +- Do not revert another agent's edits. + +## Cleanup + +Only the owner/finalizer lane runs `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. If blocked, append `BLOCKED:` with branch, task, blocker, next, evidence. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/proposal.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/proposal.md new file mode 100644 index 0000000..5828f3f --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/proposal.md @@ -0,0 +1,15 @@ +# Proposal: architect (agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/specs/architect/spec.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/specs/architect/spec.md new file mode 100644 index 0000000..7b1d664 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/specs/architect/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: architect + +## ADDED Requirements + +### Requirement: architect responsibilities for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +This role MUST define and deliver its scoped outputs with evidence. + +#### Scenario: Role executes assigned scope +- **WHEN** the role begins execution for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/tasks.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/tasks.md new file mode 100644 index 0000000..4fe6f9e --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/architect/tasks.md @@ -0,0 +1,33 @@ +# architect tasks + +## 1. Spec + +- [ ] 1.1 Define ownership boundaries, interfaces, and artifact responsibilities for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- [ ] 1.2 Validate architecture constraints and non-functional requirements coverage + +## 2. Tests + +- [ ] 2.1 Define architectural verification checkpoints (integration boundaries, failure modes, compatibility) +- [ ] 2.2 Validate that acceptance criteria map to concrete architecture decisions + +## 3. Implementation + +- [ ] 3.1 Review plan for strongest antithesis/tradeoff tensions +- [ ] 3.2 Propose synthesis path and guardrails for implementation teams +- [ ] 3.3 Record architecture sign-off notes for downstream execution + +## 4. Checkpoints + +- [ ] [A1] READY - Architecture review checkpoint + +## 5. Collaboration + +- [ ] 5.1 Owner recorded this lane before edits. +- [ ] 5.2 Record joined agents / handoffs, or mark `N/A` when solo. +- [ ] 5.3 Record unresolved plan questions in `../open-questions.md`, or mark `N/A` when none. + +## 6. Cleanup + +- [ ] 6.1 If this lane owns finalization, run `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. +- [ ] 6.2 Record PR URL + final `MERGED` state in the handoff. +- [ ] 6.3 Confirm sandbox cleanup (`git worktree list`, `git branch -a`) or append `BLOCKED:` and stop. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md new file mode 100644 index 0000000..fea19f1 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md @@ -0,0 +1,4 @@ +# Plan Checkpoints: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 + +Chronological checkpoint log for all roles. + diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/coordinator-prompt.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/coordinator-prompt.md new file mode 100644 index 0000000..8d05b2b --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/coordinator-prompt.md @@ -0,0 +1,41 @@ +# Master Coordinator Prompt + +You are the coordinator for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## Objective + +Drive this plan from draft to execution-ready status with strict checkpoint discipline and no scope drift. + +## Source-of-truth artifacts + +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/plan.md` +- role `prompt.md` files for copy/paste helper startup +- role `tasks.md` files for planner/architect/critic/executor/writer/verifier + +## Coordinator responsibilities + +1. Keep checkpoints current in each role `tasks.md` and root `checkpoints.md`. +2. Route unresolved questions and branching decisions into `open-questions.md`. +3. Ensure each role has explicit acceptance criteria and verification evidence. +4. Prevent implementation from starting before planning gates are complete. +5. Keep handoffs concise: files changed, behavior touched, verification output, risks. + +## Wave-splitting decision (optional) + +Create wave prompts in `kickoff-prompts.md` only when at least one applies: + +- 3+ independent implementation lanes can run in parallel. +- Runtime cutover/rollback sequencing needs explicit lane ownership. +- Risk is high enough that bounded execution packets reduce coordination mistakes. + +If wave splitting is not needed, keep execution under a single owner with normal role checkpoints. + +## Exit criteria + +- All role checkpoints required for planning are done. +- Execution lanes (if any) have clear ownership boundaries. +- `open-questions.md` captures unresolved decisions that still need answers. +- Verification plan and rollback expectations are explicit and testable. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/.openspec.yaml b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/.openspec.yaml new file mode 100644 index 0000000..513c6d9 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 +role: critic +status: draft +artifacts: + prompt: prompt.md + proposal: proposal.md + tasks: tasks.md + spec: specs/critic/spec.md diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/README.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/README.md new file mode 100644 index 0000000..5b5c877 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/README.md @@ -0,0 +1,12 @@ +# critic + +Role workspace for `critic`. + +Default artifacts: +- `.openspec.yaml` +- `prompt.md` +- `proposal.md` +- `tasks.md` +- `specs//spec.md` + +Use this folder for role notes, artifacts, and status updates. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/prompt.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/prompt.md new file mode 100644 index 0000000..4f171e3 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/prompt.md @@ -0,0 +1,34 @@ +# critic Prompt + +You are the `critic` role for OpenSpec plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/tasks.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/proposal.md` + +## Before edits + +1. Confirm branch/worktree with `git status --short --branch`. +2. Claim every touched file before editing: + - Prefer Colony `task_claim_file` when an active task exists. + - Otherwise run `gx locks claim --branch `. +3. Stay inside assigned files/modules; coordinate before touching shared paths. + +## Working rules + +- Update `critic/tasks.md` as each item completes. +- Record durable unresolved questions in `open-questions.md`. +- Keep handoffs short: files changed, behavior touched, verification, risks. +- Do not revert another agent's edits. + +## Cleanup + +Only the owner/finalizer lane runs `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. If blocked, append `BLOCKED:` with branch, task, blocker, next, evidence. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/proposal.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/proposal.md new file mode 100644 index 0000000..b527bd1 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/proposal.md @@ -0,0 +1,15 @@ +# Proposal: critic (agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/specs/critic/spec.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/specs/critic/spec.md new file mode 100644 index 0000000..1155bcb --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/specs/critic/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: critic + +## ADDED Requirements + +### Requirement: critic responsibilities for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +This role MUST define and deliver its scoped outputs with evidence. + +#### Scenario: Role executes assigned scope +- **WHEN** the role begins execution for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/tasks.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/tasks.md new file mode 100644 index 0000000..6b455a2 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/critic/tasks.md @@ -0,0 +1,33 @@ +# critic tasks + +## 1. Spec + +- [ ] 1.1 Validate principle-driver-option consistency across the plan +- [ ] 1.2 Validate risks, consequences, and mitigation clarity (including idempotency expectations) + +## 2. Tests + +- [ ] 2.1 Validate testability and measurability of all acceptance criteria +- [ ] 2.2 Validate verification steps are concrete and reproducible + +## 3. Implementation + +- [ ] 3.1 Produce verdict (APPROVE / ITERATE / REJECT) with actionable feedback +- [ ] 3.2 Confirm revised drafts resolve prior findings before approval +- [ ] 3.3 Publish final quality/risk sign-off notes + +## 4. Checkpoints + +- [ ] [C1] READY - Quality gate checkpoint + +## 5. Collaboration + +- [ ] 5.1 Owner recorded this lane before edits. +- [ ] 5.2 Record joined agents / handoffs, or mark `N/A` when solo. +- [ ] 5.3 Record unresolved plan questions in `../open-questions.md`, or mark `N/A` when none. + +## 6. Cleanup + +- [ ] 6.1 If this lane owns finalization, run `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. +- [ ] 6.2 Record PR URL + final `MERGED` state in the handoff. +- [ ] 6.3 Confirm sandbox cleanup (`git worktree list`, `git branch -a`) or append `BLOCKED:` and stop. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/.openspec.yaml b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/.openspec.yaml new file mode 100644 index 0000000..b083c53 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 +role: executor +status: draft +artifacts: + prompt: prompt.md + proposal: proposal.md + tasks: tasks.md + spec: specs/executor/spec.md diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/README.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/README.md new file mode 100644 index 0000000..62b95ee --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/README.md @@ -0,0 +1,12 @@ +# executor + +Role workspace for `executor`. + +Default artifacts: +- `.openspec.yaml` +- `prompt.md` +- `proposal.md` +- `tasks.md` +- `specs//spec.md` + +Use this folder for role notes, artifacts, and status updates. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/checkpoints.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/checkpoints.md new file mode 100644 index 0000000..1f69805 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/checkpoints.md @@ -0,0 +1,4 @@ +# executor checkpoints + +Timestamped execution checkpoints for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/prompt.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/prompt.md new file mode 100644 index 0000000..12b7cdf --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/prompt.md @@ -0,0 +1,34 @@ +# executor Prompt + +You are the `executor` role for OpenSpec plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/tasks.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/proposal.md` + +## Before edits + +1. Confirm branch/worktree with `git status --short --branch`. +2. Claim every touched file before editing: + - Prefer Colony `task_claim_file` when an active task exists. + - Otherwise run `gx locks claim --branch `. +3. Stay inside assigned files/modules; coordinate before touching shared paths. + +## Working rules + +- Update `executor/tasks.md` as each item completes. +- Record durable unresolved questions in `open-questions.md`. +- Keep handoffs short: files changed, behavior touched, verification, risks. +- Do not revert another agent's edits. + +## Cleanup + +Only the owner/finalizer lane runs `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. If blocked, append `BLOCKED:` with branch, task, blocker, next, evidence. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/proposal.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/proposal.md new file mode 100644 index 0000000..7468228 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/proposal.md @@ -0,0 +1,15 @@ +# Proposal: executor (agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/specs/executor/spec.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/specs/executor/spec.md new file mode 100644 index 0000000..2bdcb8e --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/specs/executor/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: executor + +## ADDED Requirements + +### Requirement: executor responsibilities for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +This role MUST define and deliver its scoped outputs with evidence. + +#### Scenario: Role executes assigned scope +- **WHEN** the role begins execution for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/tasks.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/tasks.md new file mode 100644 index 0000000..062ff29 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/executor/tasks.md @@ -0,0 +1,33 @@ +# executor tasks + +## 1. Spec + +- [ ] 1.1 Map approved plan requirements to concrete implementation work items +- [ ] 1.2 Validate touched components/files are explicitly listed before coding starts + +## 2. Tests + +- [ ] 2.1 Define test additions/updates required to lock intended behavior +- [ ] 2.2 Validate regression and smoke verification commands for delivery + +## 3. Implementation + +- [ ] 3.1 Execute implementation tasks in approved order +- [ ] 3.2 Keep progress and evidence linked back to plan checkpoints +- [ ] 3.3 Complete final verification bundle for handoff + +## 4. Checkpoints + +- [ ] [E1] READY - Execution start checkpoint + +## 5. Collaboration + +- [ ] 5.1 Owner recorded this lane before edits. +- [ ] 5.2 Record joined agents / handoffs, or mark `N/A` when solo. +- [ ] 5.3 Record unresolved plan questions in `../open-questions.md`, or mark `N/A` when none. + +## 6. Cleanup + +- [ ] 6.1 If this lane owns finalization, run `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. +- [ ] 6.2 Record PR URL + final `MERGED` state in the handoff. +- [ ] 6.3 Confirm sandbox cleanup (`git worktree list`, `git branch -a`) or append `BLOCKED:` and stop. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/kickoff-prompts.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/kickoff-prompts.md new file mode 100644 index 0000000..3eba92d --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/kickoff-prompts.md @@ -0,0 +1,108 @@ +# Kickoff Prompts (Copy/Paste) + +Use these only when the coordinator decides wave-splitting is needed. + +## Prompt A — Wave A (Primary lane) + +```text +You own Wave-A for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` in /home/deadpool/Documents/codex-lb. + +Goal: +Implement the assigned Wave-A scope and return verification evidence. + +Hard constraints: +- You are not alone in the codebase; do not revert others' work. +- Stay in your owned files/modules only. +- Record explicit handoff notes for integration. + +Owned scope: +- + +Verification: +- + +Handoff format: +- Files changed +- Behavior touched +- Verification outputs +- Risks/follow-ups +``` + +## Prompt B — Wave B (Secondary lane) + +```text +You own Wave-B for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` in /home/deadpool/Documents/codex-lb. + +Goal: +Implement the assigned Wave-B scope and return verification evidence. + +Hard constraints: +- You are not alone in the codebase; do not revert others' work. +- Stay in your owned files/modules only. +- Record explicit handoff notes for integration. + +Owned scope: +- + +Verification: +- + +Handoff format: +- Files changed +- Behavior touched +- Verification outputs +- Risks/follow-ups +``` + +## Prompt C — Wave C (Secondary lane) + +```text +You own Wave-C for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` in /home/deadpool/Documents/codex-lb. + +Goal: +Implement the assigned Wave-C scope and return verification evidence. + +Hard constraints: +- You are not alone in the codebase; do not revert others' work. +- Stay in your owned files/modules only. +- Record explicit handoff notes for integration. + +Owned scope: +- + +Verification: +- + +Handoff format: +- Files changed +- Behavior touched +- Verification outputs +- Risks/follow-ups +``` + +## Prompt D — Integrator lane + +```text +You are the integrator for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` in /home/deadpool/Documents/codex-lb. + +Goal: +Integrate completed waves, resolve conflicts, run final verification, and prepare rollout/cutover notes. + +Hard constraints: +- You are not alone in the codebase; do not revert others' work. +- Preserve safety-critical behavior unless explicitly planned and tested. +- Keep final output evidence-first. + +Owned scope: +- integration glue and shared touchpoints +- final validation + handoff summary + +Verification: +- + +Final report: +- Files changed +- Integration decisions +- Verification outputs +- Remaining risks +``` diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md new file mode 100644 index 0000000..24b0738 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md @@ -0,0 +1,6 @@ +# Open Questions: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 + +Capture unresolved plan questions here as unchecked checklist items. +Keep each item concrete, decision-shaped, and easy to close with evidence. + +- [ ] Add the next unresolved question here. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/phases.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/phases.md new file mode 100644 index 0000000..06596f7 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/phases.md @@ -0,0 +1,15 @@ +# Plan Phases: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 + +One entry per phase. Checkbox marks map to: `x` = completed, `>` = in progress, space = pending. +Indented sub-bullets are optional metadata consumed by the Plans UI: + +- `session`: which agent kind runs the phase (`codex` / `claude`). +- `checkpoints`: comma-separated role checkpoint ids delivered within the phase. +- `summary`: one short sentence rendered under the phase title. + +One phase is intended to fit into a single Codex or Claude session task. + +- [ ] [PH01] First milestone title goes here + - session: codex + - checkpoints: P1, A1 + - summary: Describe the single session outcome expected for this phase. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/.openspec.yaml b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/.openspec.yaml new file mode 100644 index 0000000..49fd0b3 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 +role: planner +status: draft +artifacts: + prompt: prompt.md + proposal: proposal.md + tasks: tasks.md + spec: specs/planner/spec.md diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/README.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/README.md new file mode 100644 index 0000000..1b0ad5d --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/README.md @@ -0,0 +1,12 @@ +# planner + +Role workspace for `planner`. + +Default artifacts: +- `.openspec.yaml` +- `prompt.md` +- `proposal.md` +- `tasks.md` +- `specs//spec.md` + +Use this folder for role notes, artifacts, and status updates. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/plan.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/plan.md new file mode 100644 index 0000000..1e6f962 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/plan.md @@ -0,0 +1,65 @@ +# ExecPlan: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 + +This ExecPlan is a living document. Keep `Progress`, `Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` current as work proceeds. + +Follow repository guidance in `openspec/plan/PLANS.md`. + +## Purpose / Big Picture + +Describe what becomes possible after this plan is executed and how a user/operator can observe it working. + +## Progress + +- [ ] (YYYY-MM-DD HH:MMZ) Capture initial scope and acceptance criteria. +- [ ] (YYYY-MM-DD HH:MMZ) Draft architecture/tradeoff plan and verification strategy. +- [ ] (YYYY-MM-DD HH:MMZ) Finalize execution-ready handoff. + +## Surprises & Discoveries + +- Observation: _none yet_ + Evidence: _n/a_ + +## Decision Log + +- Decision: Use OpenSpec plan workspace as source of truth for this planning cycle. + Rationale: Keeps planning artifacts in-repo and reviewable. + Date/Author: YYYY-MM-DD / planner + +## Outcomes & Retrospective + +Summarize outcomes, gaps, and lessons learned when a milestone or the full plan is completed. + +## Context and Orientation + +Describe relevant modules, files, constraints, and assumptions for a newcomer. Use repository-relative paths. + +## Plan of Work + +Describe the sequence of edits and deliverables in prose. Name target files and expected effects. + +## Concrete Steps + +List exact commands with working directory and short expected outcomes. + + cd /home/deadpool/Documents/codex-lb + openspec validate --specs + +## Validation and Acceptance + +State observable behavior and verification evidence required before execution handoff. + +## Idempotence and Recovery + +Document safe re-run behavior, rollback strategy, and failure recovery notes. + +## Artifacts and Notes + +Capture concise command output snippets, evidence pointers, and references. + +## Interfaces and Dependencies + +Name concrete interfaces/modules/dependencies and any required signatures/contracts. + +## Revision Note + +- YYYY-MM-DD HH:MMZ: Initial scaffold generated by `scripts/openspec/init-plan-workspace.sh`. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/prompt.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/prompt.md new file mode 100644 index 0000000..90391f0 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/prompt.md @@ -0,0 +1,34 @@ +# planner Prompt + +You are the `planner` role for OpenSpec plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/tasks.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/proposal.md` + +## Before edits + +1. Confirm branch/worktree with `git status --short --branch`. +2. Claim every touched file before editing: + - Prefer Colony `task_claim_file` when an active task exists. + - Otherwise run `gx locks claim --branch `. +3. Stay inside assigned files/modules; coordinate before touching shared paths. + +## Working rules + +- Update `planner/tasks.md` as each item completes. +- Record durable unresolved questions in `open-questions.md`. +- Keep handoffs short: files changed, behavior touched, verification, risks. +- Do not revert another agent's edits. + +## Cleanup + +Only the owner/finalizer lane runs `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. If blocked, append `BLOCKED:` with branch, task, blocker, next, evidence. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/proposal.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/proposal.md new file mode 100644 index 0000000..41e04d4 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/proposal.md @@ -0,0 +1,15 @@ +# Proposal: planner (agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/specs/planner/spec.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/specs/planner/spec.md new file mode 100644 index 0000000..cc90c9d --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/specs/planner/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: planner + +## ADDED Requirements + +### Requirement: planner responsibilities for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +This role MUST define and deliver its scoped outputs with evidence. + +#### Scenario: Role executes assigned scope +- **WHEN** the role begins execution for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/tasks.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/tasks.md new file mode 100644 index 0000000..f1ad886 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/planner/tasks.md @@ -0,0 +1,33 @@ +# planner tasks + +## 1. Spec + +- [ ] 1.1 Define planning principles, decision drivers, and viable options for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- [ ] 1.2 Validate that scope, constraints, and acceptance criteria are captured in `summary.md` + +## 2. Tests + +- [ ] 2.1 Define verification approach for plan quality (traceability, testability, evidence expectations) +- [ ] 2.2 Validate OpenSpec consistency checkpoints (including `openspec validate --specs` when applicable) + +## 3. Implementation + +- [ ] 3.1 Produce the initial RALPLAN-DR plan draft +- [ ] 3.2 Integrate Architect/Critic feedback into revised plan iterations +- [ ] 3.3 Publish final planning handoff with explicit execution lanes + +## 4. Checkpoints + +- [ ] [P1] READY - Initial planning draft checkpoint + +## 5. Collaboration + +- [ ] 5.1 Owner recorded this lane before edits. +- [ ] 5.2 Record joined agents / handoffs, or mark `N/A` when solo. +- [ ] 5.3 Record unresolved plan questions in `../open-questions.md`, or mark `N/A` when none. + +## 6. Cleanup + +- [ ] 6.1 If this lane owns finalization, run `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. +- [ ] 6.2 Record PR URL + final `MERGED` state in the handoff. +- [ ] 6.3 Confirm sandbox cleanup (`git worktree list`, `git branch -a`) or append `BLOCKED:` and stop. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md new file mode 100644 index 0000000..3f9d892 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md @@ -0,0 +1,8 @@ +# Plan Summary: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 + +- **Mode:** ralplan +- **Status:** draft + +## Context + +Describe the planning context, constraints, and desired outcomes. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/.openspec.yaml b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/.openspec.yaml new file mode 100644 index 0000000..376017d --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 +role: verifier +status: draft +artifacts: + prompt: prompt.md + proposal: proposal.md + tasks: tasks.md + spec: specs/verifier/spec.md diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/README.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/README.md new file mode 100644 index 0000000..1daa373 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/README.md @@ -0,0 +1,12 @@ +# verifier + +Role workspace for `verifier`. + +Default artifacts: +- `.openspec.yaml` +- `prompt.md` +- `proposal.md` +- `tasks.md` +- `specs//spec.md` + +Use this folder for role notes, artifacts, and status updates. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/prompt.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/prompt.md new file mode 100644 index 0000000..32ac9e7 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/prompt.md @@ -0,0 +1,34 @@ +# verifier Prompt + +You are the `verifier` role for OpenSpec plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/tasks.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/proposal.md` + +## Before edits + +1. Confirm branch/worktree with `git status --short --branch`. +2. Claim every touched file before editing: + - Prefer Colony `task_claim_file` when an active task exists. + - Otherwise run `gx locks claim --branch `. +3. Stay inside assigned files/modules; coordinate before touching shared paths. + +## Working rules + +- Update `verifier/tasks.md` as each item completes. +- Record durable unresolved questions in `open-questions.md`. +- Keep handoffs short: files changed, behavior touched, verification, risks. +- Do not revert another agent's edits. + +## Cleanup + +Only the owner/finalizer lane runs `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. If blocked, append `BLOCKED:` with branch, task, blocker, next, evidence. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/proposal.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/proposal.md new file mode 100644 index 0000000..6e1c365 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/proposal.md @@ -0,0 +1,15 @@ +# Proposal: verifier (agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/specs/verifier/spec.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/specs/verifier/spec.md new file mode 100644 index 0000000..70581a3 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/specs/verifier/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: verifier + +## ADDED Requirements + +### Requirement: verifier responsibilities for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +This role MUST define and deliver its scoped outputs with evidence. + +#### Scenario: Role executes assigned scope +- **WHEN** the role begins execution for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/tasks.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/tasks.md new file mode 100644 index 0000000..abecdc2 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/verifier/tasks.md @@ -0,0 +1,33 @@ +# verifier tasks + +## 1. Spec + +- [ ] 1.1 Define end-to-end validation matrix for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- [ ] 1.2 Validate success/failure conditions and evidence requirements + +## 2. Tests + +- [ ] 2.1 Execute verification commands and collect outputs +- [ ] 2.2 Validate idempotency/re-run behavior and error-path handling + +## 3. Implementation + +- [ ] 3.1 Verify completed work against acceptance criteria +- [ ] 3.2 Produce pass/fail findings with concrete evidence links +- [ ] 3.3 Publish final verification sign-off (or blocker report) + +## 4. Checkpoints + +- [ ] [V1] READY - Verification checkpoint + +## 5. Collaboration + +- [ ] 5.1 Owner recorded this lane before edits. +- [ ] 5.2 Record joined agents / handoffs, or mark `N/A` when solo. +- [ ] 5.3 Record unresolved plan questions in `../open-questions.md`, or mark `N/A` when none. + +## 6. Cleanup + +- [ ] 6.1 If this lane owns finalization, run `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. +- [ ] 6.2 Record PR URL + final `MERGED` state in the handoff. +- [ ] 6.3 Confirm sandbox cleanup (`git worktree list`, `git branch -a`) or append `BLOCKED:` and stop. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/.openspec.yaml b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/.openspec.yaml new file mode 100644 index 0000000..3c177b0 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39 +role: writer +status: draft +artifacts: + prompt: prompt.md + proposal: proposal.md + tasks: tasks.md + spec: specs/writer/spec.md diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/README.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/README.md new file mode 100644 index 0000000..757c824 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/README.md @@ -0,0 +1,12 @@ +# writer + +Role workspace for `writer`. + +Default artifacts: +- `.openspec.yaml` +- `prompt.md` +- `proposal.md` +- `tasks.md` +- `specs//spec.md` + +Use this folder for role notes, artifacts, and status updates. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/prompt.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/prompt.md new file mode 100644 index 0000000..da43363 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/prompt.md @@ -0,0 +1,34 @@ +# writer Prompt + +You are the `writer` role for OpenSpec plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/summary.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/open-questions.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/tasks.md` +- `openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/proposal.md` + +## Before edits + +1. Confirm branch/worktree with `git status --short --branch`. +2. Claim every touched file before editing: + - Prefer Colony `task_claim_file` when an active task exists. + - Otherwise run `gx locks claim --branch `. +3. Stay inside assigned files/modules; coordinate before touching shared paths. + +## Working rules + +- Update `writer/tasks.md` as each item completes. +- Record durable unresolved questions in `open-questions.md`. +- Keep handoffs short: files changed, behavior touched, verification, risks. +- Do not revert another agent's edits. + +## Cleanup + +Only the owner/finalizer lane runs `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. If blocked, append `BLOCKED:` with branch, task, blocker, next, evidence. diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/proposal.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/proposal.md new file mode 100644 index 0000000..1cac447 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/proposal.md @@ -0,0 +1,15 @@ +# Proposal: writer (agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/specs/writer/spec.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/specs/writer/spec.md new file mode 100644 index 0000000..2b2c018 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/specs/writer/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: writer + +## ADDED Requirements + +### Requirement: writer responsibilities for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +This role MUST define and deliver its scoped outputs with evidence. + +#### Scenario: Role executes assigned scope +- **WHEN** the role begins execution for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/tasks.md b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/tasks.md new file mode 100644 index 0000000..154ca92 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39/writer/tasks.md @@ -0,0 +1,33 @@ +# writer tasks + +## 1. Spec + +- [ ] 1.1 Validate documentation scope and audience for `agent-claude-masterplan-tui-fleet-pane-health-2026-05-15-23-39` +- [ ] 1.2 Validate consistency between plan terminology and OpenSpec artifacts + +## 2. Tests + +- [ ] 2.1 Define documentation verification checklist (accuracy, completeness, command correctness) +- [ ] 2.2 Validate command/help text examples against current workflow behavior + +## 3. Implementation + +- [ ] 3.1 Update workflow docs and command guidance for approved plan behavior +- [ ] 3.2 Add or refine examples for operator usage and handoff clarity +- [ ] 3.3 Publish final docs change summary with references + +## 4. Checkpoints + +- [ ] [W1] READY - Docs update checkpoint + +## 5. Collaboration + +- [ ] 5.1 Owner recorded this lane before edits. +- [ ] 5.2 Record joined agents / handoffs, or mark `N/A` when solo. +- [ ] 5.3 Record unresolved plan questions in `../open-questions.md`, or mark `N/A` when none. + +## 6. Cleanup + +- [ ] 6.1 If this lane owns finalization, run `gx branch finish --branch --base dev --via-pr --wait-for-merge --cleanup`. +- [ ] 6.2 Record PR URL + final `MERGED` state in the handoff. +- [ ] 6.3 Confirm sandbox cleanup (`git worktree list`, `git branch -a`) or append `BLOCKED:` and stop. diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 1506057..e7ab941 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -413,6 +413,15 @@ dependencies = [ "ratatui", ] +[[package]] +name = "fleet-pane-health" +version = "0.0.1" +dependencies = [ + "crossterm", + "fleet-ui", + "ratatui", +] + [[package]] name = "fleet-plan-tree" version = "0.0.1" diff --git a/rust/fleet-pane-health/Cargo.toml b/rust/fleet-pane-health/Cargo.toml new file mode 100644 index 0000000..29c5dca --- /dev/null +++ b/rust/fleet-pane-health/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "fleet-pane-health" +version = "0.0.1" +edition = "2021" +publish = false +description = "Per-pane health dashboard for codex-fleet." + +[[bin]] +name = "fleet-pane-health" +path = "src/main.rs" + +[dependencies] +ratatui = "0.30" +crossterm = "0.29" +fleet-ui = { path = "../fleet-ui" } diff --git a/rust/fleet-pane-health/src/main.rs b/rust/fleet-pane-health/src/main.rs new file mode 100644 index 0000000..0f67739 --- /dev/null +++ b/rust/fleet-pane-health/src/main.rs @@ -0,0 +1,755 @@ +// fleet-pane-health — per-pane health dashboard for codex-fleet. +// +// Each row corresponds to a live tmux pane in the fleet session and shows: +// - pane id (e.g. "%337") +// - the pane's `@panel` user-option label (codex-fleet's per-pane agent tag) +// - last-activity age, derived from the mtime of any +// /tmp/claude-viz/{kiro,claude,codex}-worker-.log file matching the +// panel id (newest one wins) +// - colony claim state, parsed best-effort from +// /tmp/claude-viz/colony-claims.json (falls back to "unknown") +// - cap-probe state, parsed best-effort from +// /tmp/claude-viz/cap-probe-cache/.json (verdict + freshness) +// +// All data sources are best-effort and READ-ONLY. We never write to /tmp. +// If tmux is unavailable or no fleet session is up, the dashboard renders +// an empty list with an explanatory message in the header. +// +// Refresh cadence: poll every 1s (spec). Quit on `q` or Esc. + +use std::collections::HashMap; +use std::ffi::OsStr; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::time::{Duration, Instant, SystemTime}; + +use crossterm::event::{self, Event, KeyCode, KeyEventKind}; +use fleet_ui::palette::*; +use ratatui::layout::{Constraint, Direction, Layout, Rect}; +use ratatui::style::{Modifier, Style}; +use ratatui::text::{Line, Span}; +use ratatui::widgets::{Block, BorderType, Borders, Paragraph}; +use ratatui::{DefaultTerminal, Frame}; + +const POLL_INTERVAL: Duration = Duration::from_secs(1); +const VIZ_ROOT: &str = "/tmp/claude-viz"; +const CAP_PROBE_DIR: &str = "/tmp/claude-viz/cap-probe-cache"; +const COLONY_CLAIMS: &str = "/tmp/claude-viz/colony-claims.json"; + +#[derive(Clone, Debug)] +struct PaneRow { + pane_id: String, // e.g. "%337" + panel: String, // @panel label or "—" + last_activity: Option, // seconds since now (0 = just now) + activity_source: String, // file name for the freshest log + colony_claim: String, // "claimed:" | "free" | "unknown" + cap_probe: String, // "ok" / "429" / "unknown" + cap_probe_age: Option, // seconds since mtime +} + +#[derive(Clone, Debug, Default)] +struct Snapshot { + rows: Vec, + note: Option, // diagnostic shown in header when fleet is empty + captured_at: Option, +} + +fn main() -> io::Result<()> { + let mut terminal = ratatui::init(); + let result = run(&mut terminal); + ratatui::restore(); + result +} + +fn run(terminal: &mut DefaultTerminal) -> io::Result<()> { + let mut snapshot = collect_snapshot(); + let mut last_refresh = Instant::now(); + + loop { + terminal.draw(|frame| render(frame, frame.area(), &snapshot))?; + + // Poll for keys with a short timeout so we can refresh at POLL_INTERVAL. + let remaining = POLL_INTERVAL.saturating_sub(last_refresh.elapsed()); + let wait = remaining.min(Duration::from_millis(100)); + if event::poll(wait)? { + if let Event::Key(key) = event::read()? { + if key.kind == KeyEventKind::Press { + match key.code { + KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => return Ok(()), + _ => {} + } + } + } + } + + if last_refresh.elapsed() >= POLL_INTERVAL { + snapshot = collect_snapshot(); + last_refresh = Instant::now(); + } + } +} + +// --------------------------------------------------------------------------- +// Data gathering +// --------------------------------------------------------------------------- + +fn collect_snapshot() -> Snapshot { + let session = std::env::var("CODEX_FLEET_SESSION").unwrap_or_else(|_| "codex-fleet".into()); + let panes = tmux_panes(&session); + + let log_index = build_log_index(); + let claim_index = read_colony_claims(); + let cap_index = read_cap_probe_cache(); + + let note = if panes.is_empty() { + Some(format!( + "no panes from tmux session '{session}' — set CODEX_FLEET_SESSION or start the fleet" + )) + } else { + None + }; + + let mut rows: Vec = panes + .into_iter() + .map(|(pane_id, panel)| { + let activity = log_index.get(&panel).cloned().or_else(|| { + // Fall back to a best-effort substring match against any log key. + log_index + .iter() + .find(|(k, _)| !panel.is_empty() && k.contains(&panel)) + .map(|(_, v)| v.clone()) + }); + let (last_activity, activity_source) = match activity { + Some(a) => (Some(a.age_secs), a.file_name), + None => (None, String::from("—")), + }; + + let colony_claim = claim_index + .get(&panel) + .cloned() + .unwrap_or_else(|| "unknown".to_string()); + + // cap-probe is keyed by email — we don't have a direct pane→email map. + // If panel happens to contain an "@" we treat it as an email; otherwise + // we show the freshest probe across the cache. + let cap_entry = if panel.contains('@') { + cap_index.get(&panel).cloned() + } else { + cap_index + .values() + .min_by_key(|e| e.age_secs) + .cloned() + }; + let (cap_probe, cap_probe_age) = match cap_entry { + Some(e) => (e.verdict, Some(e.age_secs)), + None => ("unknown".into(), None), + }; + + PaneRow { + pane_id, + panel, + last_activity, + activity_source, + colony_claim, + cap_probe, + cap_probe_age, + } + }) + .collect(); + + rows.sort_by(|a, b| a.panel.cmp(&b.panel)); + + Snapshot { + rows, + note, + captured_at: Some(SystemTime::now()), + } +} + +/// `tmux list-panes -s -t -F '#{pane_id}\t#{@panel}'` — returns +/// (pane_id, panel) tuples. Empty list when tmux is absent or session missing. +fn tmux_panes(session: &str) -> Vec<(String, String)> { + let output = Command::new("tmux") + .args([ + "list-panes", + "-s", + "-t", + session, + "-F", + "#{pane_id}\t#{@panel}", + ]) + .output(); + let Ok(out) = output else { return Vec::new() }; + if !out.status.success() { + return Vec::new(); + } + String::from_utf8_lossy(&out.stdout) + .lines() + .filter_map(|line| { + let mut parts = line.splitn(2, '\t'); + let pane_id = parts.next()?.trim().to_owned(); + let panel = parts.next().unwrap_or("").trim().to_owned(); + if pane_id.is_empty() { + None + } else { + Some(( + pane_id, + if panel.is_empty() { + "—".to_owned() + } else { + panel + }, + )) + } + }) + .collect() +} + +#[derive(Clone, Debug)] +struct LogActivity { + age_secs: u64, + file_name: String, +} + +/// Walk /tmp/claude-viz for `{kiro,claude,codex}-worker-.log` and index +/// the *freshest* match per worker id. The "" portion is whatever follows +/// the worker prefix; we use it as a panel key for direct lookup. +fn build_log_index() -> HashMap { + let mut idx: HashMap = HashMap::new(); + let now = SystemTime::now(); + let Ok(entries) = fs::read_dir(VIZ_ROOT) else { + return idx; + }; + for entry in entries.flatten() { + let path = entry.path(); + let Some(name) = path.file_name().and_then(OsStr::to_str) else { + continue; + }; + let Some(id) = parse_worker_log(name) else { + continue; + }; + let mtime = match entry.metadata().and_then(|m| m.modified()) { + Ok(m) => m, + Err(_) => continue, + }; + let age_secs = now.duration_since(mtime).map(|d| d.as_secs()).unwrap_or(0); + let candidate = LogActivity { + age_secs, + file_name: name.to_owned(), + }; + idx.entry(id) + .and_modify(|cur| { + if age_secs < cur.age_secs { + *cur = candidate.clone(); + } + }) + .or_insert(candidate); + } + idx +} + +/// Parse names like: +/// "claude-worker-claude-fleet-1.log" -> Some("claude-fleet-1") +/// "kiro-worker-foo.log" -> Some("foo") +/// "codex-worker-acct-3.log" -> Some("acct-3") +/// Everything else returns None. +fn parse_worker_log(name: &str) -> Option { + let stem = name.strip_suffix(".log")?; + for prefix in ["claude-worker-", "kiro-worker-", "codex-worker-"] { + if let Some(id) = stem.strip_prefix(prefix) { + if id.is_empty() { + return None; + } + return Some(id.to_owned()); + } + } + None +} + +/// Read /tmp/claude-viz/colony-claims.json. Format is best-effort: we look +/// for an outer JSON object and try a couple of common shapes: +/// { "": { "task": "...", ... }, ... } +/// { "": "task-id-string", ... } +/// On any parse failure we return an empty map (callers fall back to +/// "unknown"). +fn read_colony_claims() -> HashMap { + let mut out: HashMap = HashMap::new(); + let Ok(text) = fs::read_to_string(COLONY_CLAIMS) else { + return out; + }; + // Tiny, dependency-free JSON object scanner: find each top-level + // "key": pair. This is intentionally conservative — anything + // remotely tricky falls back to "unknown". + let trimmed = text.trim(); + if !trimmed.starts_with('{') || !trimmed.ends_with('}') { + return out; + } + let body = &trimmed[1..trimmed.len() - 1]; + let mut depth: i32 = 0; + let mut in_str = false; + let mut esc = false; + let mut start = 0usize; + let bytes = body.as_bytes(); + let mut segments: Vec<&str> = Vec::new(); + for (i, &c) in bytes.iter().enumerate() { + if esc { + esc = false; + continue; + } + match c { + b'\\' if in_str => esc = true, + b'"' => in_str = !in_str, + b'{' | b'[' if !in_str => depth += 1, + b'}' | b']' if !in_str => depth -= 1, + b',' if !in_str && depth == 0 => { + segments.push(&body[start..i]); + start = i + 1; + } + _ => {} + } + } + if start < body.len() { + segments.push(&body[start..]); + } + for seg in segments { + let Some(colon) = seg.find(':') else { continue }; + let raw_key = seg[..colon].trim().trim_matches('"'); + let raw_val = seg[colon + 1..].trim(); + if raw_key.is_empty() { + continue; + } + let label = if raw_val.starts_with('"') { + let val = raw_val.trim_matches('"'); + format!("claimed:{val}") + } else if raw_val.starts_with('{') { + // Try to pluck a "task" field, otherwise just say "claimed". + extract_field(raw_val, "task") + .map(|t| format!("claimed:{t}")) + .unwrap_or_else(|| "claimed".into()) + } else if raw_val.eq_ignore_ascii_case("null") { + "free".into() + } else { + "claimed".into() + }; + out.insert(raw_key.to_string(), label); + } + out +} + +/// Extract a string field from a flat JSON object literal like +/// `{ "task": "abc", ... }`. Returns None on any parse hiccup. +fn extract_field(obj: &str, key: &str) -> Option { + let needle = format!("\"{key}\""); + let idx = obj.find(&needle)?; + let rest = &obj[idx + needle.len()..]; + let colon = rest.find(':')?; + let after = rest[colon + 1..].trim_start(); + let after = after.strip_prefix('"')?; + let end = after.find('"')?; + Some(after[..end].to_owned()) +} + +#[derive(Clone, Debug)] +struct CapProbe { + verdict: String, + age_secs: u64, +} + +/// Walk /tmp/claude-viz/cap-probe-cache/*.json. Each file is one account. +/// We pull `"verdict"` and the file's mtime; anything that fails to parse is +/// reported as "unknown". +fn read_cap_probe_cache() -> HashMap { + let mut idx: HashMap = HashMap::new(); + let now = SystemTime::now(); + let Ok(entries) = fs::read_dir(CAP_PROBE_DIR) else { + return idx; + }; + for entry in entries.flatten() { + let path: PathBuf = entry.path(); + let Some(name) = path.file_stem().and_then(OsStr::to_str) else { + continue; + }; + if path.extension().and_then(OsStr::to_str) != Some("json") { + continue; + } + let mtime = match entry.metadata().and_then(|m| m.modified()) { + Ok(m) => m, + Err(_) => continue, + }; + let age_secs = now.duration_since(mtime).map(|d| d.as_secs()).unwrap_or(0); + let verdict = read_verdict(&path).unwrap_or_else(|| "unknown".into()); + idx.insert(name.to_owned(), CapProbe { verdict, age_secs }); + } + idx +} + +fn read_verdict(path: &Path) -> Option { + let text = fs::read_to_string(path).ok()?; + extract_field(&text, "verdict") + .map(|v| match v.as_str() { + "healthy" | "ok" => "ok".into(), + "rate_limited" | "throttled" | "429" => "429".into(), + other => other.to_string(), + }) +} + +// --------------------------------------------------------------------------- +// Rendering +// --------------------------------------------------------------------------- + +fn render(frame: &mut Frame, area: Rect, snap: &Snapshot) { + // Solid background fill so the dashboard reads as a card on the page. + frame.render_widget( + Block::default().style(Style::default().bg(IOS_BG_SOLID)), + area, + ); + + let chunks = Layout::default() + .direction(Direction::Vertical) + .constraints([ + Constraint::Length(2), // header (caption + sub-line) + Constraint::Length(1), // top hairline divider + Constraint::Length(1), // column headings + Constraint::Length(1), // hairline divider below headings + Constraint::Min(0), // pane rows + Constraint::Length(1), // footer + ]) + .split(area); + + render_header(frame, chunks[0], snap); + hairline(frame, chunks[1]); + render_column_headings(frame, chunks[2]); + hairline(frame, chunks[3]); + render_rows(frame, chunks[4], &snap.rows); + render_footer(frame, chunks[5], snap); +} + +fn render_header(frame: &mut Frame, area: Rect, snap: &Snapshot) { + if area.height == 0 { + return; + } + let count = snap.rows.len(); + let title = Line::from(vec![ + Span::styled( + "PANE HEALTH", + Style::default() + .fg(IOS_TINT) + .bg(IOS_BG_SOLID) + .add_modifier(Modifier::BOLD), + ), + Span::styled( + format!(" {count} pane{}", if count == 1 { "" } else { "s" }), + Style::default().fg(IOS_FG).bg(IOS_BG_SOLID), + ), + ]); + frame.render_widget( + Paragraph::new(title), + Rect { + x: area.x, + y: area.y, + width: area.width, + height: 1, + }, + ); + + if area.height >= 2 { + let sub = match &snap.note { + Some(note) => Line::from(Span::styled( + clip(note, area.width.saturating_sub(1)), + Style::default().fg(IOS_ORANGE).bg(IOS_BG_SOLID), + )), + None => Line::from(Span::styled( + "polling tmux + /tmp/claude-viz every 1s · read-only", + Style::default().fg(IOS_FG_MUTED).bg(IOS_BG_SOLID), + )), + }; + frame.render_widget( + Paragraph::new(sub), + Rect { + x: area.x, + y: area.y + 1, + width: area.width, + height: 1, + }, + ); + } +} + +fn hairline(frame: &mut Frame, area: Rect) { + if area.height == 0 { + return; + } + let block = Block::default() + .borders(Borders::TOP) + .border_type(BorderType::Plain) + .border_style(Style::default().fg(IOS_HAIRLINE)) + .style(Style::default().bg(IOS_BG_SOLID)); + frame.render_widget(block, area); +} + +fn render_column_headings(frame: &mut Frame, area: Rect) { + let cols = column_layout(area); + let style = Style::default() + .fg(IOS_FG_MUTED) + .bg(IOS_BG_SOLID) + .add_modifier(Modifier::BOLD); + let names = ["PANE", "PANEL", "ACTIVITY", "COLONY", "CAP-PROBE"]; + for (rect, name) in cols.into_iter().zip(names.iter()) { + frame.render_widget( + Paragraph::new(Line::from(Span::styled( + clip(name, rect.width.saturating_sub(1)), + style, + ))), + rect, + ); + } +} + +fn render_rows(frame: &mut Frame, area: Rect, rows: &[PaneRow]) { + if area.height == 0 { + return; + } + if rows.is_empty() { + let y = area.y + area.height / 2; + frame.render_widget( + Paragraph::new(Line::from(Span::styled( + "no panes to show — fleet session not running or empty", + Style::default().fg(IOS_FG_FAINT).bg(IOS_BG_SOLID), + ))), + Rect { + x: area.x + 1, + y, + width: area.width.saturating_sub(2), + height: 1, + }, + ); + return; + } + let limit = area.height as usize; + for (i, row) in rows.iter().take(limit).enumerate() { + let y = area.y + i as u16; + let row_area = Rect { + x: area.x, + y, + width: area.width, + height: 1, + }; + // Alternating row background mirrors the iOS grouped-list look. + let row_bg = if i % 2 == 0 { + IOS_ROW_BG_DARK + } else { + IOS_ROW_BG_LIGHT + }; + frame.render_widget( + Block::default().style(Style::default().bg(row_bg)), + row_area, + ); + + let cols = column_layout(row_area); + // 1. pane id + frame.render_widget( + Paragraph::new(Span::styled( + clip(&row.pane_id, cols[0].width.saturating_sub(1)), + Style::default() + .fg(IOS_TINT) + .bg(row_bg) + .add_modifier(Modifier::BOLD), + )), + cols[0], + ); + // 2. panel label + frame.render_widget( + Paragraph::new(Span::styled( + clip(&row.panel, cols[1].width.saturating_sub(1)), + Style::default().fg(IOS_FG).bg(row_bg), + )), + cols[1], + ); + // 3. activity age + file + let (age_text, age_color) = activity_label(row.last_activity); + let activity_line = Line::from(vec![ + Span::styled(age_text, Style::default().fg(age_color).bg(row_bg)), + Span::styled(" ", Style::default().bg(row_bg)), + Span::styled( + clip( + &row.activity_source, + cols[2].width.saturating_sub(12), + ), + Style::default().fg(IOS_FG_FAINT).bg(row_bg), + ), + ]); + frame.render_widget(Paragraph::new(activity_line), cols[2]); + // 4. colony claim + let claim_color = if row.colony_claim.starts_with("claimed") { + IOS_PURPLE + } else if row.colony_claim == "free" { + IOS_GREEN + } else { + IOS_FG_FAINT + }; + frame.render_widget( + Paragraph::new(Span::styled( + clip(&row.colony_claim, cols[3].width.saturating_sub(1)), + Style::default().fg(claim_color).bg(row_bg), + )), + cols[3], + ); + // 5. cap-probe + age + let cap_color = match row.cap_probe.as_str() { + "ok" => IOS_GREEN, + "429" => IOS_DESTRUCTIVE, + "unknown" => IOS_FG_FAINT, + _ => IOS_YELLOW, + }; + let cap_text = match row.cap_probe_age { + Some(age) => format!("{} · {}", row.cap_probe, age_words(age)), + None => row.cap_probe.clone(), + }; + frame.render_widget( + Paragraph::new(Span::styled( + clip(&cap_text, cols[4].width.saturating_sub(1)), + Style::default().fg(cap_color).bg(row_bg), + )), + cols[4], + ); + } +} + +fn render_footer(frame: &mut Frame, area: Rect, snap: &Snapshot) { + if area.height == 0 { + return; + } + let captured = snap + .captured_at + .and_then(|t| t.duration_since(SystemTime::UNIX_EPOCH).ok()) + .map(|d| format!("captured {}s ago", elapsed_label(d.as_secs()))) + .unwrap_or_else(|| "captured —".into()); + let left = " q / Esc quit · 1s poll "; + let right = format!("{captured} "); + let right_w = right.chars().count() as u16; + let left_w = area.width.saturating_sub(right_w); + frame.render_widget( + Paragraph::new(Line::from(vec![ + Span::styled( + clip(left, left_w), + Style::default().fg(IOS_FG_MUTED).bg(IOS_BG_SOLID), + ), + Span::styled( + format!("{:>width$}", right, width = right_w as usize), + Style::default().fg(IOS_FG_FAINT).bg(IOS_BG_SOLID), + ), + ])), + area, + ); +} + +fn column_layout(area: Rect) -> Vec { + // Reserve a 1-cell gutter on the left so column text isn't flush with the + // edge. Constraints are roughly proportional and degrade gracefully on + // narrow terminals. + let inner = Rect { + x: area.x.saturating_add(1), + y: area.y, + width: area.width.saturating_sub(2), + height: area.height, + }; + Layout::default() + .direction(Direction::Horizontal) + .constraints([ + Constraint::Length(8), // pane id + Constraint::Min(14), // panel label (flex) + Constraint::Length(28), // activity + Constraint::Length(22), // colony claim + Constraint::Length(20), // cap-probe + ]) + .split(inner) + .to_vec() +} + +fn activity_label(secs: Option) -> (String, ratatui::style::Color) { + match secs { + None => ("—".to_string(), IOS_FG_FAINT), + Some(0) => ("just now".to_string(), IOS_GREEN), + Some(s) if s < 60 => (format!("{s}s ago"), IOS_GREEN), + Some(s) if s < 600 => (format!("{}m ago", s / 60), IOS_YELLOW), + Some(s) if s < 3600 => (format!("{}m ago", s / 60), IOS_ORANGE), + Some(s) => (format!("{}h ago", s / 3600), IOS_DESTRUCTIVE), + } +} + +fn age_words(secs: u64) -> String { + if secs < 60 { + format!("{secs}s") + } else if secs < 3600 { + format!("{}m", secs / 60) + } else { + format!("{}h", secs / 3600) + } +} + +fn elapsed_label(_epoch_secs: u64) -> String { + // We don't carry "delta vs now" here — the snapshot is fresh by definition + // (we re-render every tick), so this surfaces a `0s` heartbeat label. + "0".to_string() +} + +fn clip(input: &str, width: u16) -> String { + if width == 0 { + return String::new(); + } + let chars: Vec = input.chars().collect(); + if chars.len() <= width as usize { + return input.to_owned(); + } + if width == 1 { + return "…".into(); + } + let mut out: String = chars + .into_iter() + .take(width.saturating_sub(1) as usize) + .collect(); + out.push('…'); + out +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parses_worker_log_names() { + assert_eq!( + parse_worker_log("claude-worker-claude-fleet-1.log").as_deref(), + Some("claude-fleet-1") + ); + assert_eq!( + parse_worker_log("kiro-worker-foo.log").as_deref(), + Some("foo") + ); + assert_eq!( + parse_worker_log("codex-worker-acct-3.log").as_deref(), + Some("acct-3") + ); + assert_eq!(parse_worker_log("cap-probe.log"), None); + assert_eq!(parse_worker_log("random.txt"), None); + } + + #[test] + fn extracts_verdict_field() { + let body = r#"{"verdict": "healthy", "until_epoch": 0}"#; + assert_eq!(extract_field(body, "verdict").as_deref(), Some("healthy")); + let none = r#"{"other": 1}"#; + assert_eq!(extract_field(none, "verdict"), None); + } + + #[test] + fn activity_label_buckets() { + assert_eq!(activity_label(None).0, "—"); + assert_eq!(activity_label(Some(0)).0, "just now"); + assert_eq!(activity_label(Some(45)).0, "45s ago"); + assert_eq!(activity_label(Some(120)).0, "2m ago"); + assert_eq!(activity_label(Some(7200)).0, "2h ago"); + } +}