aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/superpowers/plans/2026-07-04-list-directional-color-delreq.md346
1 files changed, 346 insertions, 0 deletions
diff --git a/docs/superpowers/plans/2026-07-04-list-directional-color-delreq.md b/docs/superpowers/plans/2026-07-04-list-directional-color-delreq.md
new file mode 100644
index 0000000..3345053
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-04-list-directional-color-delreq.md
@@ -0,0 +1,346 @@
+# `-l` directional color + DelReq column Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** In the `-l` table, color the newer version cell green (hint vs SBo `.info`), keep equal-version rows yellow, and add a `DelReq` boolean column before `Created`.
+
+**Architecture:** All changes live in `list_hint_files` in `mkhint`. A green color pair is added next to the existing yellow. Per row, byte-equality keeps the yellow-row + `MATCHED_PKGS` path; on a difference `sort -V` picks the newer side and only that cell is wrapped green. Version cells are pre-padded to width 22 before wrapping so color bytes don't break alignment. A `DelReq` column (`✓` / blank) is derived from a `DELREQUIRES="..."` grep.
+
+**Tech Stack:** bash, coreutils (`sort -V`), `tput`, existing `tests/mkhint_test.sh` harness.
+
+---
+
+## File structure
+
+- Modify: `mkhint` — `list_hint_files` (lines ~97-158): color setup, header, per-row logic, legend.
+- Modify: `tests/mkhint_test.sh` — append T60-T64 after T59 (~line 1145).
+- Modify: `CLAUDE.md` — update the `--list` / `-l` behavior bullet and the test table.
+- Modify: `README.md` — update `-l` description if it enumerates columns/colors.
+
+No new files. No new dependencies.
+
+---
+
+## Task 1: Green color pair + directional cell coloring
+
+**Files:**
+- Modify: `mkhint` `list_hint_files` (color block ~104-111, header ~115, row loop ~118-147, legend ~155-157)
+- Test: `tests/mkhint_test.sh` (append T60, T61, T62)
+
+- [ ] **Step 1: Write failing tests T60, T61, T62**
+
+Append after T59 in `tests/mkhint_test.sh`:
+
+```bash
+# ── T60: -l hint newer than SBo → HintVer cell green ─────────────────────────
+echo ""
+echo "T60: -l hint version newer than .info → HintVer green"
+rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
+# curl .info is 8.5.0; hint 8.6.0 is newer
+cat > "$MOCK_HINT/curl.hint" << 'EOF'
+VERSION="8.6.0"
+ARCH="x86_64"
+DOWNLOAD="https://curl.se/download/curl-8.6.0.tar.gz"
+MD5SUM="abc123def456abc123def456abc123de"
+EOF
+out=$(MKHINT_FORCE_COLOR=1 run_mkhint -l 2>&1)
+# green start code (ESC[32m) appears before the hint version 8.6.0 on the curl row
+echo "$out" | grep curl.hint | grep -q $'\033\[32m.*8\.6\.0' \
+ && { echo " PASS: HintVer green when hint newer"; (( PASS++ )); } \
+ || { echo " FAIL: HintVer not green"; echo "$out" | grep curl.hint | cat -v | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T60 green"); }
+# whole row must NOT be yellow (differing versions)
+echo "$out" | grep curl.hint | grep -q $'\033\[33m' \
+ && { echo " FAIL: differing row wrongly yellow"; (( FAIL++ )); ERRORS+=("T60 yellow"); } \
+ || { echo " PASS: differing row not yellow"; (( PASS++ )); }
+
+# ── T61: -l SBo newer than hint → SBOVer cell green ──────────────────────────
+echo ""
+echo "T61: -l .info version newer than hint → SBOVer green"
+rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
+# curl .info is 8.5.0; hint 8.4.0 is older
+cat > "$MOCK_HINT/curl.hint" << 'EOF'
+VERSION="8.4.0"
+ARCH="x86_64"
+DOWNLOAD="https://curl.se/download/curl-8.4.0.tar.gz"
+MD5SUM="abc123def456abc123def456abc123de"
+EOF
+out=$(MKHINT_FORCE_COLOR=1 run_mkhint -l 2>&1)
+# green start code appears before the SBo version 8.5.0 on the curl row
+echo "$out" | grep curl.hint | grep -q $'\033\[32m.*8\.5\.0' \
+ && { echo " PASS: SBOVer green when .info newer"; (( PASS++ )); } \
+ || { echo " FAIL: SBOVer not green"; echo "$out" | grep curl.hint | cat -v | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T61 green"); }
+
+# ── T62: -l equal versions → whole row yellow, no green ──────────────────────
+echo ""
+echo "T62: -l equal versions → yellow row, no green cell"
+rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
+cat > "$MOCK_HINT/curl.hint" << 'EOF'
+VERSION="8.5.0"
+ARCH="x86_64"
+DOWNLOAD="https://curl.se/download/curl-8.5.0.tar.gz"
+MD5SUM="abc123def456abc123def456abc123de"
+EOF
+out=$(MKHINT_FORCE_COLOR=1 run_mkhint -l 2>&1)
+echo "$out" | grep curl.hint | grep -q $'\033\[33m' \
+ && { echo " PASS: equal row yellow"; (( PASS++ )); } \
+ || { echo " FAIL: equal row not yellow"; echo "$out" | grep curl.hint | cat -v | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T62 yellow"); }
+echo "$out" | grep curl.hint | grep -q $'\033\[32m' \
+ && { echo " FAIL: equal row wrongly green"; (( FAIL++ )); ERRORS+=("T62 green"); } \
+ || { echo " PASS: equal row has no green"; (( PASS++ )); }
+```
+
+- [ ] **Step 2: Run tests, verify they fail**
+
+Run: `bash tests/mkhint_test.sh 2>&1 | grep -A1 'T6[012]'`
+Expected: FAIL lines for T60 green, T61 green (no green codes emitted yet). T62 yellow passes (existing behavior), T62 green passes.
+
+- [ ] **Step 3: Add green color pair to the color setup block**
+
+In `mkhint`, replace the color block (currently ~104-111):
+
+```bash
+ # Color only on a TTY, or when forced for tests. tput optional.
+ local c_on="" c_off="" g_on=""
+ if [[ -n "$MKHINT_FORCE_COLOR" || -t 1 ]]; then
+ if command -v tput &>/dev/null && tput setaf 3 &>/dev/null; then
+ c_on=$(tput setaf 3); g_on=$(tput setaf 2); c_off=$(tput sgr0)
+ else
+ c_on=$'\033[33m'; g_on=$'\033[32m'; c_off=$'\033[0m'
+ fi
+ fi
+```
+
+`c_on` = yellow (row), `g_on` = green (cell), `c_off` = reset (shared).
+
+- [ ] **Step 4: Rewrite the per-row loop for directional coloring**
+
+Replace the row body (currently ~136-145, from `local row` through `count=$((count + 1))`) with:
+
+```bash
+ local hv sv
+ hv=$(printf "%22s" "$VER")
+ sv=$(printf "%22s" "$SBO_VER")
+ if [[ -n "$SBO_VER" && "$VER" == "$SBO_VER" ]]; then
+ # equal → whole row yellow
+ local row
+ row=$(printf "%-40s %s %s %-20s" "$name" "$hv" "$sv" "$category")
+ printf "%s%s%s\n" "$c_on" "$row" "$c_off"
+ MATCHED_PKGS+=("$pkg")
+ matched=$((matched + 1))
+ else
+ # differ (or no SBO_VER) → green the newer cell, row plain
+ if [[ -n "$SBO_VER" ]]; then
+ local newer
+ newer=$(printf '%s\n%s\n' "$VER" "$SBO_VER" | sort -V | tail -1)
+ if [[ "$newer" == "$VER" ]]; then
+ hv="${g_on}${hv}${c_off}"
+ else
+ sv="${g_on}${sv}${c_off}"
+ fi
+ fi
+ printf "%-40s %s %s %-20s\n" "$name" "$hv" "$sv" "$category"
+ fi
+ count=$((count + 1))
+```
+
+Note: the trailing `Category`/`Created` columns are handled in Task 2 (DelReq column insertion). For now this task drops `date` from the printf to keep the diff focused; Task 2 adds `DelReq` + `Created` back. If executing Task 1 standalone, append ` %s` and `"$date"` to both printf lines and their format strings.
+
+- [ ] **Step 5: Update the legend**
+
+Replace the matched-count legend (currently ~155-157):
+
+```bash
+ if [[ $matched -gt 0 && -n "$c_on" ]]; then
+ echo "(yellow row = versions match; green = newer side)"
+ fi
+```
+
+Keep grep-compatible text; T36 greps `"highlighted = hint version matches"` — update T36 in the same commit (see Step 6).
+
+- [ ] **Step 6: Fix T36 legend grep**
+
+T36 (line ~823) greps for the old legend string. Change:
+
+```bash
+echo "$out" | grep -q "yellow row = versions match" \
+```
+
+- [ ] **Step 7: Run tests, verify T60/T61/T62 and T36 pass**
+
+Run: `bash tests/mkhint_test.sh 2>&1 | grep -E 'T(36|6[012])|FAIL'`
+Expected: all PASS, no FAIL.
+
+- [ ] **Step 8: bash -n check**
+
+Run: `bash -n mkhint && echo OK`
+Expected: `OK`
+
+- [ ] **Step 9: Commit**
+
+```bash
+git add mkhint tests/mkhint_test.sh
+git commit -S -m "feat: -l colors newer version cell green, keeps equal rows yellow"
+```
+
+---
+
+## Task 2: DelReq column
+
+**Files:**
+- Modify: `mkhint` `list_hint_files` (header ~115, row loop printf lines from Task 1)
+- Test: `tests/mkhint_test.sh` (append T63, T64)
+
+- [ ] **Step 1: Write failing tests T63, T64**
+
+Append after T62 in `tests/mkhint_test.sh`:
+
+```bash
+# ── T63: -l shows ✓ in DelReq for hint with DELREQUIRES ──────────────────────
+echo ""
+echo "T63: -l DelReq column shows ✓ when DELREQUIRES populated"
+rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
+cat > "$MOCK_HINT/curl.hint" << 'EOF'
+VERSION="8.5.0"
+ARCH="x86_64"
+DELREQUIRES="rust-opt"
+DOWNLOAD="https://curl.se/download/curl-8.5.0.tar.gz"
+MD5SUM="abc123def456abc123def456abc123de"
+EOF
+out=$(run_mkhint -l 2>&1)
+echo "$out" | grep -q "DelReq" \
+ && { echo " PASS: DelReq header present"; (( PASS++ )); } \
+ || { echo " FAIL: DelReq header missing"; echo "$out" | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T63 header"); }
+echo "$out" | grep curl.hint | grep -q "✓" \
+ && { echo " PASS: ✓ shown for DELREQUIRES hint"; (( PASS++ )); } \
+ || { echo " FAIL: ✓ missing"; echo "$out" | grep curl.hint | sed 's/^/ /'; (( FAIL++ )); ERRORS+=("T63 check"); }
+
+# ── T64: -l DelReq blank when no DELREQUIRES ─────────────────────────────────
+echo ""
+echo "T64: -l DelReq column blank when no DELREQUIRES"
+rm -f "$MOCK_HINT"/*.hint "$MOCK_HINT"/*.bak 2>/dev/null
+cat > "$MOCK_HINT/curl.hint" << 'EOF'
+VERSION="8.5.0"
+ARCH="x86_64"
+DOWNLOAD="https://curl.se/download/curl-8.5.0.tar.gz"
+MD5SUM="abc123def456abc123def456abc123de"
+EOF
+out=$(run_mkhint -l 2>&1)
+echo "$out" | grep curl.hint | grep -q "✓" \
+ && { echo " FAIL: ✓ shown without DELREQUIRES"; (( FAIL++ )); ERRORS+=("T64 check"); } \
+ || { echo " PASS: no ✓ without DELREQUIRES"; (( PASS++ )); }
+```
+
+- [ ] **Step 2: Run tests, verify they fail**
+
+Run: `bash tests/mkhint_test.sh 2>&1 | grep -A2 'T6[34]'`
+Expected: FAIL for T63 header (no DelReq column yet), T63 check. T64 passes vacuously.
+
+- [ ] **Step 3: Add the DelReq header**
+
+Replace the header printf (currently ~115):
+
+```bash
+ printf "%-40s %22s %22s %-20s %-6s %s\n" "File" "HintVer" "SBOVer" "Category" "DelReq" "Created"
+```
+
+Column order: `File | HintVer | SBOVer | Category | DelReq | Created`.
+
+- [ ] **Step 4: Compute the DelReq value in the row loop**
+
+After the `VER` skip check in the loop (after line ~124), add:
+
+```bash
+ local delreq=""
+ grep -q '^DELREQUIRES="..*"' "$file" && delreq="✓"
+```
+
+- [ ] **Step 5: Add a display-width pad helper and use it for DelReq + Created**
+
+Add this helper just above `list_hint_files` (near line 96):
+
+```bash
+# Pad a possibly-multibyte glyph (0 or 1 display column) to a fixed width.
+_pad_glyph() {
+ local g="$1" w="$2"
+ if [[ -n "$g" ]]; then printf "%s%*s" "$g" $((w - 1)) ""; else printf "%*s" "$w" ""; fi
+}
+```
+
+In both row printf calls from Task 1, append the DelReq + Created columns.
+Yellow (equal) branch:
+
+```bash
+ row=$(printf "%-40s %s %s %-20s %s %s" "$name" "$hv" "$sv" "$category" "$(_pad_glyph "$delreq" 6)" "$date")
+```
+
+Plain (differ) branch:
+
+```bash
+ printf "%-40s %s %s %-20s %s %s\n" "$name" "$hv" "$sv" "$category" "$(_pad_glyph "$delreq" 6)" "$date"
+```
+
+(`$date` is already computed at ~135; keep it.)
+
+- [ ] **Step 6: Run tests, verify T63/T64 pass**
+
+Run: `bash tests/mkhint_test.sh 2>&1 | grep -E 'T6[34]|FAIL'`
+Expected: all PASS, no FAIL.
+
+- [ ] **Step 7: Full suite + bash -n**
+
+Run: `bash -n mkhint && bash tests/mkhint_test.sh 2>&1 | tail -5`
+Expected: `OK`, and the summary reports 0 failures.
+
+- [ ] **Step 8: Commit**
+
+```bash
+git add mkhint tests/mkhint_test.sh
+git commit -S -m "feat: -l adds DelReq column showing populated DELREQUIRES"
+```
+
+---
+
+## Task 3: Docs
+
+**Files:**
+- Modify: `CLAUDE.md` (the `--list` / `-l` behavior bullet, the test-coverage table)
+- Modify: `README.md` (if the `-l` description enumerates columns)
+
+- [ ] **Step 1: Update CLAUDE.md `--list` bullet**
+
+Find the `- \`--list\` / \`-l\`:` bullet under "Key Behaviors". Update the color description: rows with equal versions are yellow; when versions differ the newer side's cell is green (via `sort -V`); a `DelReq` column (before `Created`) shows `✓` when the hint has a populated `DELREQUIRES`. Legend now reads `(yellow row = versions match; green = newer side)`.
+
+- [ ] **Step 2: Add T60-T64 to the CLAUDE.md test table**
+
+Append rows:
+
+```
+| T60 | `-l` hint newer than `.info` — HintVer cell green, row not yellow |
+| T61 | `-l` `.info` newer than hint — SBOVer cell green |
+| T62 | `-l` equal versions — whole row yellow, no green |
+| T63 | `-l` hint with populated `DELREQUIRES` — `✓` in DelReq column |
+| T64 | `-l` hint without `DELREQUIRES` — DelReq blank |
+```
+
+- [ ] **Step 3: Update README.md if needed**
+
+Check the `-l` / `--list` section. If it describes the highlight behavior or columns, update it to mention directional green + the DelReq column. If README does not enumerate columns, no change.
+
+Run: `grep -n 'list\|highlight\|HintVer' README.md`
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add CLAUDE.md README.md
+git commit -S -m "docs: document -l directional color and DelReq column"
+```
+
+---
+
+## Self-review notes
+
+- Spec coverage: directional color (Task 1), equal→yellow kept (Task 1), DelReq column (Task 2), legend (Task 1), tests T60-T64 (Tasks 1-2), docs (Task 3). All covered.
+- `MATCHED_PKGS` / `matched` only populated on the equal branch — preserves `-R` behavior.
+- `sort -V` verified present and correct on target box (orders `1.10.0 > 1.2.3`, `2026_06_10 > 2026_06_02`).
+- Color-byte alignment: version cells pre-padded to 22 before green wrap; DelReq via `_pad_glyph` for multibyte `✓`.
+- Task 1 Step 4 note flags the date-column dependency between Task 1 and Task 2 so tasks stay runnable in order.