diff --git a/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/.openspec.yaml b/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/.openspec.yaml new file mode 100644 index 0000000..9f70866 --- /dev/null +++ b/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-15 diff --git a/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/proposal.md b/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/proposal.md new file mode 100644 index 0000000..f2d941a --- /dev/null +++ b/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/proposal.md @@ -0,0 +1,19 @@ +## Why + +- `fleet-pane-health` lists every tmux pane in the fleet session but does not surface which agent kind (codex / kiro / claude) owns the pane. The operator has to infer it from the `@panel` label, which is fine for the three present codex panels but breaks down once the fleet runs a mix of all three. +- A single-glance distinction between codex, kiro and claude is the most-asked-for improvement to the live dashboard — see `scripts/codex-fleet/full-bringup.sh` already spawning all three kinds. + +## What Changes + +- Add an `AgentKind` classifier (`codex` / `kiro` / `claude` / `unknown`) that derives the kind from the `@panel` label first and falls back to the `/tmp/claude-viz/{codex,kiro,claude}-worker-*.log` filename. +- Render a new `KIND` column between `PANE` and `PANEL` showing a colored badge (`CODX` blue / `KIRO` purple / `CLAU` orange) on each row. +- Add a `g` keybinding that toggles a grouped view: rows are sorted by kind and a `── group: ──` header (in the kind's tint) is injected between groups. +- Footer reports `g group: on|off` so the current view mode is always visible. +- Cover the classifier and the grouped/ungrouped renderers with unit tests plus a ratatui `TestBackend` integration test that asserts each kind's badge and group header lands in the rendered buffer. + +## Impact + +- Surfaces affected: `rust/fleet-pane-health/src/main.rs` only (no schema, no shared crate). Read-only data paths unchanged. +- Risk: low. The classifier is panel-driven so a stale log file cannot mis-label a pane. The grouping toggle starts off, preserving the existing row order at startup. +- Rollout: ship in the next PR; no scripts, supervisors, or config need to change. +- Follow-ups (not in this change): a `f` keybinding to filter to one kind; pulling the badge widget into `fleet-ui` once a second consumer needs it. diff --git a/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/specs/fleet-pane-health-badge-column/spec.md b/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/specs/fleet-pane-health-badge-column/spec.md new file mode 100644 index 0000000..aa925c4 --- /dev/null +++ b/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/specs/fleet-pane-health-badge-column/spec.md @@ -0,0 +1,44 @@ +## ADDED Requirements + +### Requirement: AgentKind classification for fleet-pane-health rows +The `fleet-pane-health` dashboard SHALL classify every pane it lists into one of `codex`, `kiro`, `claude`, or `unknown`. The classifier SHALL derive the kind from the `@panel` label first (substring match on `codex` / `kiro` / `claude`, case-insensitive) and SHALL fall back to the `/tmp/claude-viz/{codex,kiro,claude}-worker-*.log` filename prefix only when the panel label is empty or unrecognised. Panel-derived classification SHALL win over log-derived classification when both are available. + +#### Scenario: Panel label drives classification +- **WHEN** a pane has `@panel = "codex-admin-mite"` +- **THEN** its kind is `codex` +- **AND** the row renders with the `CODX` badge in the `KIND` column. + +#### Scenario: Log filename fallback when panel is blank +- **WHEN** a pane has no `@panel` label and the freshest matching log file is named `kiro-worker-foo.log` +- **THEN** its kind is `kiro` +- **AND** the row renders with the `KIRO` badge. + +#### Scenario: Unknown when neither signal matches +- **WHEN** a pane has neither a recognised panel label nor a matching worker log +- **THEN** its kind is `unknown` +- **AND** the row renders the `—` badge in the `KIND` column. + +### Requirement: KIND column rendering +The dashboard SHALL render a `KIND` column between `PANE` and `PANEL`. The column SHALL show the kind's four-character badge (`CODX`, `KIRO`, `CLAU`, or `—`) in the kind's tint colour (`IOS_TINT`, `IOS_PURPLE`, `IOS_ORANGE`, `IOS_FG_FAINT` respectively). + +#### Scenario: Column heading present +- **WHEN** the dashboard renders any frame +- **THEN** the column-headings row includes the literal `KIND` between `PANE` and `PANEL`. + +### Requirement: Group-by-kind toggle +The dashboard SHALL support a grouped view, toggled by pressing `g` or `G`. When grouped, rows SHALL be sorted by kind in the order `codex`, `kiro`, `claude`, `unknown`, and a `── group: ──` header SHALL be inserted before each group of rows. The header SHALL render in the kind's tint colour. The footer SHALL show `g group: on` when grouped and `g group: off` otherwise. + +#### Scenario: Toggling grouping injects headers +- **WHEN** the user presses `g` from the default (ungrouped) view +- **THEN** the next frame shows `── group: codex ──` (and equivalent headers for any other kinds present) and the footer reads `g group: on`. + +#### Scenario: Toggling back removes headers +- **WHEN** the user presses `g` again from the grouped view +- **THEN** no `── group: ──` header rows appear in the next frame and the footer reads `g group: off`. + +### Requirement: Read-only data flow preserved +This change SHALL NOT introduce any new writes to `/tmp/claude-viz`, any new tmux commands, or any new external processes. All data sources remain best-effort reads as before. + +#### Scenario: No writes to /tmp/claude-viz +- **WHEN** the dashboard runs for any length of time +- **THEN** no file under `/tmp/claude-viz` is created, modified, or deleted by `fleet-pane-health`. diff --git a/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/tasks.md b/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/tasks.md new file mode 100644 index 0000000..72f1e38 --- /dev/null +++ b/openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/tasks.md @@ -0,0 +1,35 @@ +## 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-fleet-pane-health-badge-column-2026-05-16-00-22`; branch=`agent//`; scope=`TODO`; action=`continue this sandbox or finish cleanup after a usage-limit/manual takeover`. +- Copy prompt: Continue `agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22` on branch `agent//`. Work inside the existing sandbox, review `openspec/changes/agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22/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 + +- [x] 1.1 Finalize proposal scope and acceptance criteria for `agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22`. +- [x] 1.2 Define normative requirements in `specs/fleet-pane-health-badge-column/spec.md`. + +## 2. Implementation + +- [x] 2.1 Implement scoped behavior changes (AgentKind classifier, KIND column, `g` toggle, footer state). +- [x] 2.2 Add/update focused regression coverage (5 classifier/grouping unit tests + 2 ratatui `TestBackend` render tests). + +## 3. Verification + +- [x] 3.1 `cargo test -p fleet-pane-health` → 10 passed, 0 failed. +- [x] 3.2 Live verification: respawn the `viz` tmux window with the patched binary, confirm `CODX`/`CLAU` badges render and `g` toggles `── group: codex ──` headers. +- [x] 3.3 Run `openspec validate agent-claude-fleet-pane-health-badge-column-2026-05-16-00-22 --type change --strict` → `Change ... is valid`. +- [x] 3.4 Run `openspec validate --specs` → `No items found to validate` (no archived specs in this repo yet). + +## 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-fleet-pane-health-badge-column-2026-05-16-00-22/README.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/README.md new file mode 100644 index 0000000..c12d2e6 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/README.md @@ -0,0 +1,30 @@ +# Plan Workspace: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 + +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-fleet-pane-health-badge-column-2026-05-16-00-22/architect/.openspec.yaml b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/.openspec.yaml new file mode 100644 index 0000000..d079582 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 +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-fleet-pane-health-badge-column-2026-05-16-00-22/architect/README.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/README.md new file mode 100644 index 0000000..1e1c275 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/architect/prompt.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/prompt.md new file mode 100644 index 0000000..965a864 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/prompt.md @@ -0,0 +1,34 @@ +# architect Prompt + +You are the `architect` role for OpenSpec plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/tasks.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/architect/proposal.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/proposal.md new file mode 100644 index 0000000..86f46f5 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/proposal.md @@ -0,0 +1,15 @@ +# Proposal: architect (agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/specs/architect/spec.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/specs/architect/spec.md new file mode 100644 index 0000000..419784d --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/specs/architect/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: architect + +## ADDED Requirements + +### Requirement: architect responsibilities for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22` +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-fleet-pane-health-badge-column-2026-05-16-00-22` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/tasks.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/tasks.md new file mode 100644 index 0000000..7c4dcc4 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/architect/tasks.md @@ -0,0 +1,33 @@ +# architect tasks + +## 1. Spec + +- [ ] 1.1 Define ownership boundaries, interfaces, and artifact responsibilities for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22` +- [ ] 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-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md new file mode 100644 index 0000000..0834010 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md @@ -0,0 +1,4 @@ +# Plan Checkpoints: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 + +Chronological checkpoint log for all roles. + diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/coordinator-prompt.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/coordinator-prompt.md new file mode 100644 index 0000000..6463f8f --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/coordinator-prompt.md @@ -0,0 +1,41 @@ +# Master Coordinator Prompt + +You are the coordinator for plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## 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-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/critic/.openspec.yaml b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/.openspec.yaml new file mode 100644 index 0000000..0169891 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 +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-fleet-pane-health-badge-column-2026-05-16-00-22/critic/README.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/README.md new file mode 100644 index 0000000..5b5c877 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/critic/prompt.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/prompt.md new file mode 100644 index 0000000..ddb418d --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/prompt.md @@ -0,0 +1,34 @@ +# critic Prompt + +You are the `critic` role for OpenSpec plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/tasks.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/critic/proposal.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/proposal.md new file mode 100644 index 0000000..450ee68 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/proposal.md @@ -0,0 +1,15 @@ +# Proposal: critic (agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/specs/critic/spec.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/specs/critic/spec.md new file mode 100644 index 0000000..43081e4 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/specs/critic/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: critic + +## ADDED Requirements + +### Requirement: critic responsibilities for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22` +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-fleet-pane-health-badge-column-2026-05-16-00-22` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/tasks.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/critic/tasks.md new file mode 100644 index 0000000..6b455a2 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/executor/.openspec.yaml b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/.openspec.yaml new file mode 100644 index 0000000..23f01c8 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 +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-fleet-pane-health-badge-column-2026-05-16-00-22/executor/README.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/README.md new file mode 100644 index 0000000..62b95ee --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/executor/checkpoints.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/checkpoints.md new file mode 100644 index 0000000..62f9491 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/checkpoints.md @@ -0,0 +1,4 @@ +# executor checkpoints + +Timestamped execution checkpoints for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/prompt.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/prompt.md new file mode 100644 index 0000000..488d580 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/prompt.md @@ -0,0 +1,34 @@ +# executor Prompt + +You are the `executor` role for OpenSpec plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/tasks.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/executor/proposal.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/proposal.md new file mode 100644 index 0000000..25e4e99 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/proposal.md @@ -0,0 +1,15 @@ +# Proposal: executor (agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/specs/executor/spec.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/specs/executor/spec.md new file mode 100644 index 0000000..e396eab --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/specs/executor/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: executor + +## ADDED Requirements + +### Requirement: executor responsibilities for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22` +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-fleet-pane-health-badge-column-2026-05-16-00-22` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/tasks.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/executor/tasks.md new file mode 100644 index 0000000..062ff29 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/kickoff-prompts.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/kickoff-prompts.md new file mode 100644 index 0000000..e0e8cd2 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22` 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-fleet-pane-health-badge-column-2026-05-16-00-22` 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-fleet-pane-health-badge-column-2026-05-16-00-22` 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-fleet-pane-health-badge-column-2026-05-16-00-22` 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-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md new file mode 100644 index 0000000..30b103b --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md @@ -0,0 +1,6 @@ +# Open Questions: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 + +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-fleet-pane-health-badge-column-2026-05-16-00-22/phases.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/phases.md new file mode 100644 index 0000000..e04e559 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/phases.md @@ -0,0 +1,15 @@ +# Plan Phases: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 + +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-fleet-pane-health-badge-column-2026-05-16-00-22/planner/.openspec.yaml b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/.openspec.yaml new file mode 100644 index 0000000..6af6665 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 +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-fleet-pane-health-badge-column-2026-05-16-00-22/planner/README.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/README.md new file mode 100644 index 0000000..1b0ad5d --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/planner/plan.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/plan.md new file mode 100644 index 0000000..8011db8 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/plan.md @@ -0,0 +1,65 @@ +# ExecPlan: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 + +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-fleet-pane-health-badge-column-2026-05-16-00-22/planner/prompt.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/prompt.md new file mode 100644 index 0000000..6216e54 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/prompt.md @@ -0,0 +1,34 @@ +# planner Prompt + +You are the `planner` role for OpenSpec plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/tasks.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/planner/proposal.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/proposal.md new file mode 100644 index 0000000..6970aea --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/proposal.md @@ -0,0 +1,15 @@ +# Proposal: planner (agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/specs/planner/spec.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/specs/planner/spec.md new file mode 100644 index 0000000..c4e3385 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/specs/planner/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: planner + +## ADDED Requirements + +### Requirement: planner responsibilities for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22` +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-fleet-pane-health-badge-column-2026-05-16-00-22` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/tasks.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/planner/tasks.md new file mode 100644 index 0000000..933ad4f --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22` +- [ ] 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-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md new file mode 100644 index 0000000..f361e92 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md @@ -0,0 +1,8 @@ +# Plan Summary: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 + +- **Mode:** ralplan +- **Status:** draft + +## Context + +Describe the planning context, constraints, and desired outcomes. diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/.openspec.yaml b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/.openspec.yaml new file mode 100644 index 0000000..407be84 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 +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-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/README.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/README.md new file mode 100644 index 0000000..1daa373 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/prompt.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/prompt.md new file mode 100644 index 0000000..703e047 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/prompt.md @@ -0,0 +1,34 @@ +# verifier Prompt + +You are the `verifier` role for OpenSpec plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/tasks.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/proposal.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/proposal.md new file mode 100644 index 0000000..cb95232 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/proposal.md @@ -0,0 +1,15 @@ +# Proposal: verifier (agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/specs/verifier/spec.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/specs/verifier/spec.md new file mode 100644 index 0000000..b904512 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/specs/verifier/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: verifier + +## ADDED Requirements + +### Requirement: verifier responsibilities for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22` +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-fleet-pane-health-badge-column-2026-05-16-00-22` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/tasks.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/tasks.md new file mode 100644 index 0000000..58067c9 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/verifier/tasks.md @@ -0,0 +1,33 @@ +# verifier tasks + +## 1. Spec + +- [ ] 1.1 Define end-to-end validation matrix for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22` +- [ ] 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-fleet-pane-health-badge-column-2026-05-16-00-22/writer/.openspec.yaml b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/.openspec.yaml new file mode 100644 index 0000000..5e8a271 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/.openspec.yaml @@ -0,0 +1,9 @@ +schema: 1 +plan: agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22 +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-fleet-pane-health-badge-column-2026-05-16-00-22/writer/README.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/README.md new file mode 100644 index 0000000..757c824 --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/writer/prompt.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/prompt.md new file mode 100644 index 0000000..2ba581d --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/prompt.md @@ -0,0 +1,34 @@ +# writer Prompt + +You are the `writer` role for OpenSpec plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## Objective + +Complete only this role's assigned checklist and leave compact evidence for the coordinator. + +## Source of truth + +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/summary.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/checkpoints.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/open-questions.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/tasks.md` +- `openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/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-fleet-pane-health-badge-column-2026-05-16-00-22/writer/proposal.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/proposal.md new file mode 100644 index 0000000..7ee667a --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/proposal.md @@ -0,0 +1,15 @@ +# Proposal: writer (agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22) + +## Why + +Summarize why this role's work is required for plan `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22`. + +## What Changes + +- [ ] List the planned role-specific changes + +## Impact + +- Scope: +- Risks: +- Dependencies: diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/specs/writer/spec.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/specs/writer/spec.md new file mode 100644 index 0000000..db0b0ac --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/specs/writer/spec.md @@ -0,0 +1,10 @@ +# Capability Spec: writer + +## ADDED Requirements + +### Requirement: writer responsibilities for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22` +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-fleet-pane-health-badge-column-2026-05-16-00-22` +- **THEN** it follows `tasks.md` and records evidence for completion diff --git a/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/tasks.md b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/tasks.md new file mode 100644 index 0000000..a333fbf --- /dev/null +++ b/openspec/plan/agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22/writer/tasks.md @@ -0,0 +1,33 @@ +# writer tasks + +## 1. Spec + +- [ ] 1.1 Validate documentation scope and audience for `agent-claude-masterplan-fleet-pane-health-badge-column-2026-05-16-00-22` +- [ ] 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/fleet-pane-health/src/main.rs b/rust/fleet-pane-health/src/main.rs index 0f67739..55f772d 100644 --- a/rust/fleet-pane-health/src/main.rs +++ b/rust/fleet-pane-health/src/main.rs @@ -38,10 +38,80 @@ 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, Copy, Debug, PartialEq, Eq, Hash)] +enum AgentKind { + Codex, + Kiro, + Claude, + Unknown, +} + +impl AgentKind { + fn classify(panel: &str, activity_source: &str) -> Self { + let p = panel.to_ascii_lowercase(); + if p.contains("kiro") { + return Self::Kiro; + } + if p.contains("codex") { + return Self::Codex; + } + if p.contains("claude") { + return Self::Claude; + } + if activity_source.starts_with("kiro-worker-") { + return Self::Kiro; + } + if activity_source.starts_with("codex-worker-") { + return Self::Codex; + } + if activity_source.starts_with("claude-worker-") { + return Self::Claude; + } + Self::Unknown + } + + fn badge(&self) -> &'static str { + match self { + Self::Codex => "CODX", + Self::Kiro => "KIRO", + Self::Claude => "CLAU", + Self::Unknown => "—", + } + } + + fn color(&self) -> ratatui::style::Color { + match self { + Self::Codex => IOS_TINT, + Self::Kiro => IOS_PURPLE, + Self::Claude => IOS_ORANGE, + Self::Unknown => IOS_FG_FAINT, + } + } + + fn sort_key(&self) -> u8 { + match self { + Self::Codex => 0, + Self::Kiro => 1, + Self::Claude => 2, + Self::Unknown => 3, + } + } + + fn group_label(&self) -> &'static str { + match self { + Self::Codex => "codex", + Self::Kiro => "kiro", + Self::Claude => "claude", + Self::Unknown => "other", + } + } +} + #[derive(Clone, Debug)] struct PaneRow { pane_id: String, // e.g. "%337" panel: String, // @panel label or "—" + kind: AgentKind, // codex / kiro / claude / unknown last_activity: Option, // seconds since now (0 = just now) activity_source: String, // file name for the freshest log colony_claim: String, // "claimed:" | "free" | "unknown" @@ -56,6 +126,15 @@ struct Snapshot { captured_at: Option, } +/// View toggles. Driven by the keyboard loop, not from data sources, so it +/// survives across snapshot refreshes. +#[derive(Clone, Copy, Debug, Default)] +struct View { + /// When true, rows are sorted by [`AgentKind`] and group-header rows are + /// injected between kinds. + grouped: bool, +} + fn main() -> io::Result<()> { let mut terminal = ratatui::init(); let result = run(&mut terminal); @@ -65,10 +144,11 @@ fn main() -> io::Result<()> { fn run(terminal: &mut DefaultTerminal) -> io::Result<()> { let mut snapshot = collect_snapshot(); + let mut view = View::default(); let mut last_refresh = Instant::now(); loop { - terminal.draw(|frame| render(frame, frame.area(), &snapshot))?; + terminal.draw(|frame| render(frame, frame.area(), &snapshot, &view))?; // Poll for keys with a short timeout so we can refresh at POLL_INTERVAL. let remaining = POLL_INTERVAL.saturating_sub(last_refresh.elapsed()); @@ -78,6 +158,7 @@ fn run(terminal: &mut DefaultTerminal) -> io::Result<()> { if key.kind == KeyEventKind::Press { match key.code { KeyCode::Char('q') | KeyCode::Char('Q') | KeyCode::Esc => return Ok(()), + KeyCode::Char('g') | KeyCode::Char('G') => view.grouped = !view.grouped, _ => {} } } @@ -147,9 +228,11 @@ fn collect_snapshot() -> Snapshot { None => ("unknown".into(), None), }; + let kind = AgentKind::classify(&panel, &activity_source); PaneRow { pane_id, panel, + kind, last_activity, activity_source, colony_claim, @@ -399,7 +482,7 @@ fn read_verdict(path: &Path) -> Option { // Rendering // --------------------------------------------------------------------------- -fn render(frame: &mut Frame, area: Rect, snap: &Snapshot) { +fn render(frame: &mut Frame, area: Rect, snap: &Snapshot, view: &View) { // 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)), @@ -422,8 +505,8 @@ fn render(frame: &mut Frame, area: Rect, snap: &Snapshot) { 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); + render_rows(frame, chunks[4], &snap.rows, view); + render_footer(frame, chunks[5], snap, view); } fn render_header(frame: &mut Frame, area: Rect, snap: &Snapshot) { @@ -495,7 +578,7 @@ fn render_column_headings(frame: &mut Frame, area: Rect) { .fg(IOS_FG_MUTED) .bg(IOS_BG_SOLID) .add_modifier(Modifier::BOLD); - let names = ["PANE", "PANEL", "ACTIVITY", "COLONY", "CAP-PROBE"]; + let names = ["PANE", "KIND", "PANEL", "ACTIVITY", "COLONY", "CAP-PROBE"]; for (rect, name) in cols.into_iter().zip(names.iter()) { frame.render_widget( Paragraph::new(Line::from(Span::styled( @@ -507,7 +590,35 @@ fn render_column_headings(frame: &mut Frame, area: Rect) { } } -fn render_rows(frame: &mut Frame, area: Rect, rows: &[PaneRow]) { +/// What occupies a visible row in the dashboard body: either an actual pane +/// or a "── group: ──" header injected when grouping is on. +enum BodyLine<'a> { + Pane(&'a PaneRow), + GroupHeader(AgentKind), +} + +fn body_lines<'a>(rows: &'a [PaneRow], view: &View) -> Vec> { + if !view.grouped { + return rows.iter().map(BodyLine::Pane).collect(); + } + // Group: stable sort by kind, then keep the per-kind order intact (already + // sorted by panel from `collect_snapshot`). + let mut indices: Vec = (0..rows.len()).collect(); + indices.sort_by(|&a, &b| rows[a].kind.sort_key().cmp(&rows[b].kind.sort_key())); + let mut out: Vec> = Vec::with_capacity(rows.len() + 4); + let mut current: Option = None; + for i in indices { + let row = &rows[i]; + if current != Some(row.kind) { + out.push(BodyLine::GroupHeader(row.kind)); + current = Some(row.kind); + } + out.push(BodyLine::Pane(row)); + } + out +} + +fn render_rows(frame: &mut Frame, area: Rect, rows: &[PaneRow], view: &View) { if area.height == 0 { return; } @@ -527,8 +638,10 @@ fn render_rows(frame: &mut Frame, area: Rect, rows: &[PaneRow]) { ); return; } + let lines = body_lines(rows, view); let limit = area.height as usize; - for (i, row) in rows.iter().take(limit).enumerate() { + let mut pane_zebra = 0usize; + for (i, line) in lines.iter().take(limit).enumerate() { let y = area.y + i as u16; let row_area = Rect { x: area.x, @@ -536,88 +649,130 @@ fn render_rows(frame: &mut Frame, area: Rect, rows: &[PaneRow]) { 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], - ); + match line { + BodyLine::GroupHeader(kind) => { + // Group headers render on the solid background, full-width. + frame.render_widget( + Block::default().style(Style::default().bg(IOS_BG_SOLID)), + row_area, + ); + let label = format!("── group: {} ──", kind.group_label()); + frame.render_widget( + Paragraph::new(Line::from(Span::styled( + clip(&label, row_area.width.saturating_sub(2)), + Style::default() + .fg(kind.color()) + .bg(IOS_BG_SOLID) + .add_modifier(Modifier::BOLD), + ))), + Rect { + x: row_area.x + 1, + y: row_area.y, + width: row_area.width.saturating_sub(2), + height: 1, + }, + ); + // Don't advance the zebra-stripe counter so the next pane row + // resumes the alternation pattern from where it left off. + } + BodyLine::Pane(row) => { + // Alternating row background mirrors the iOS grouped-list look. + let row_bg = if pane_zebra % 2 == 0 { + IOS_ROW_BG_DARK + } else { + IOS_ROW_BG_LIGHT + }; + pane_zebra += 1; + 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. kind badge + frame.render_widget( + Paragraph::new(Span::styled( + clip(row.kind.badge(), cols[1].width.saturating_sub(1)), + Style::default() + .fg(row.kind.color()) + .bg(row_bg) + .add_modifier(Modifier::BOLD), + )), + cols[1], + ); + // 3. panel label + frame.render_widget( + Paragraph::new(Span::styled( + clip(&row.panel, cols[2].width.saturating_sub(1)), + Style::default().fg(IOS_FG).bg(row_bg), + )), + cols[2], + ); + // 4. 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[3].width.saturating_sub(12), + ), + Style::default().fg(IOS_FG_FAINT).bg(row_bg), + ), + ]); + frame.render_widget(Paragraph::new(activity_line), cols[3]); + // 5. 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[4].width.saturating_sub(1)), + Style::default().fg(claim_color).bg(row_bg), + )), + cols[4], + ); + // 6. 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[5].width.saturating_sub(1)), + Style::default().fg(cap_color).bg(row_bg), + )), + cols[5], + ); + } + } } } -fn render_footer(frame: &mut Frame, area: Rect, snap: &Snapshot) { +fn render_footer(frame: &mut Frame, area: Rect, snap: &Snapshot, view: &View) { if area.height == 0 { return; } @@ -626,14 +781,15 @@ fn render_footer(frame: &mut Frame, area: Rect, snap: &Snapshot) { .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 group_state = if view.grouped { "on" } else { "off" }; + let left = format!(" q / Esc quit · g group: {group_state} · 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), + clip(&left, left_w), Style::default().fg(IOS_FG_MUTED).bg(IOS_BG_SOLID), ), Span::styled( @@ -659,6 +815,7 @@ fn column_layout(area: Rect) -> Vec { .direction(Direction::Horizontal) .constraints([ Constraint::Length(8), // pane id + Constraint::Length(6), // kind badge Constraint::Min(14), // panel label (flex) Constraint::Length(28), // activity Constraint::Length(22), // colony claim @@ -752,4 +909,169 @@ mod tests { assert_eq!(activity_label(Some(120)).0, "2m ago"); assert_eq!(activity_label(Some(7200)).0, "2h ago"); } + + #[test] + fn agent_kind_classifies_from_panel() { + assert_eq!( + AgentKind::classify("codex-admin-mite", "—"), + AgentKind::Codex + ); + assert_eq!(AgentKind::classify("kiro-foo", "—"), AgentKind::Kiro); + assert_eq!( + AgentKind::classify("idle-claude-pane-3", "—"), + AgentKind::Claude + ); + assert_eq!(AgentKind::classify("—", "—"), AgentKind::Unknown); + } + + #[test] + fn agent_kind_classifies_from_log_when_panel_blank() { + assert_eq!( + AgentKind::classify("—", "claude-worker-claude-fleet-1.log"), + AgentKind::Claude + ); + assert_eq!( + AgentKind::classify("—", "codex-worker-acct-3.log"), + AgentKind::Codex + ); + assert_eq!( + AgentKind::classify("—", "kiro-worker-x.log"), + AgentKind::Kiro + ); + } + + #[test] + fn agent_kind_panel_wins_over_log() { + // codex panel paired with a stale claude-* log key still classifies as + // codex — panel label is the authoritative signal. + assert_eq!( + AgentKind::classify("codex-admin-mite", "claude-worker-claude-fleet-1.log"), + AgentKind::Codex + ); + } + + #[test] + fn grouped_body_lines_inject_headers_per_kind() { + let rows = vec![ + row("%1", "idle-claude-pane-1", AgentKind::Claude), + row("%2", "codex-admin-mite", AgentKind::Codex), + row("%3", "kiro-foo", AgentKind::Kiro), + row("%4", "codex-bia-zazrifka", AgentKind::Codex), + ]; + let lines = body_lines(&rows, &View { grouped: true }); + let kinds: Vec<&str> = lines + .iter() + .map(|l| match l { + BodyLine::GroupHeader(k) => k.group_label(), + BodyLine::Pane(r) => match r.pane_id.as_str() { + "%1" => "pane:claude", + "%2" => "pane:codex1", + "%3" => "pane:kiro", + "%4" => "pane:codex2", + _ => "pane:?", + }, + }) + .collect(); + // Sort key is Codex(0) < Kiro(1) < Claude(2); the two codex panes + // share one header. + assert_eq!( + kinds, + vec![ + "codex", + "pane:codex1", + "pane:codex2", + "kiro", + "pane:kiro", + "claude", + "pane:claude", + ] + ); + } + + #[test] + fn ungrouped_body_lines_have_no_headers() { + let rows = vec![ + row("%1", "idle-claude-pane-1", AgentKind::Claude), + row("%2", "codex-admin-mite", AgentKind::Codex), + ]; + let lines = body_lines(&rows, &View { grouped: false }); + assert_eq!(lines.len(), 2); + assert!(matches!(lines[0], BodyLine::Pane(_))); + assert!(matches!(lines[1], BodyLine::Pane(_))); + } + + fn row(pane_id: &str, panel: &str, kind: AgentKind) -> PaneRow { + PaneRow { + pane_id: pane_id.into(), + panel: panel.into(), + kind, + last_activity: None, + activity_source: "—".into(), + colony_claim: "unknown".into(), + cap_probe: "unknown".into(), + cap_probe_age: None, + } + } + + fn buffer_to_string(buf: &ratatui::buffer::Buffer) -> String { + let area = buf.area(); + let mut out = String::with_capacity((area.width as usize + 1) * area.height as usize); + for y in 0..area.height { + for x in 0..area.width { + out.push_str(buf.cell((x, y)).map(|c| c.symbol()).unwrap_or(" ")); + } + out.push('\n'); + } + out + } + + fn render_to_string(snap: &Snapshot, view: &View, w: u16, h: u16) -> String { + use ratatui::backend::TestBackend; + use ratatui::Terminal; + let backend = TestBackend::new(w, h); + let mut terminal = Terminal::new(backend).expect("terminal"); + terminal + .draw(|frame| render(frame, frame.area(), snap, view)) + .expect("draw"); + buffer_to_string(terminal.backend().buffer()) + } + + fn three_kind_snapshot() -> Snapshot { + Snapshot { + rows: vec![ + row("%1", "codex-admin-mite", AgentKind::Codex), + row("%2", "kiro-foo", AgentKind::Kiro), + row("%3", "idle-claude-pane-3", AgentKind::Claude), + ], + note: None, + captured_at: None, + } + } + + #[test] + fn render_ungrouped_shows_kind_column_for_each_agent() { + let out = render_to_string(&three_kind_snapshot(), &View::default(), 120, 10); + // KIND column heading present. + assert!(out.contains("KIND"), "missing KIND header in:\n{out}"); + // One badge per agent kind, on its row. + assert!(out.contains("CODX"), "missing CODX badge in:\n{out}"); + assert!(out.contains("KIRO"), "missing KIRO badge in:\n{out}"); + assert!(out.contains("CLAU"), "missing CLAU badge in:\n{out}"); + // No group headers when grouping is off. + assert!( + !out.contains("group: codex"), + "unexpected group header in ungrouped view:\n{out}" + ); + // Footer reports group state. + assert!(out.contains("group: off"), "missing footer state:\n{out}"); + } + + #[test] + fn render_grouped_shows_one_header_per_kind() { + let out = render_to_string(&three_kind_snapshot(), &View { grouped: true }, 120, 12); + assert!(out.contains("── group: codex ──"), "no codex header:\n{out}"); + assert!(out.contains("── group: kiro ──"), "no kiro header:\n{out}"); + assert!(out.contains("── group: claude ──"), "no claude header:\n{out}"); + assert!(out.contains("group: on"), "missing footer state:\n{out}"); + } }