diff --git a/docs/superpowers/evidence/issue-dashboard-awaiting-2026-10-02.json b/docs/superpowers/evidence/issue-dashboard-awaiting-2026-10-02.json new file mode 100644 index 00000000..4d4423d7 --- /dev/null +++ b/docs/superpowers/evidence/issue-dashboard-awaiting-2026-10-02.json @@ -0,0 +1,37 @@ +{ + "intent": "Merged-but-open issues (Merge only tags the opener, kado-no-akari #21) get their own 'Awaiting opener' column in ~/src/issue-dashboard and an 'awaiting' count in status.json; the i3bar issue_dashboard block shows it as '⏳ N' instead of counting it as to do.", + "scope": [ + "linux_configuration/i3blocks/issue_dashboard.sh - read the optional 'awaiting' field and show '· ⏳ N' between '✓' and '✗' when N > 0", + "linux_configuration/tests/test_i3blocks_issue_dashboard.sh - awaiting text, hidden at zero, ordering, colour, old-format file without the key", + "Non-goal: the dashboard change itself (local repo ~/src/issue-dashboard, commit 0e6077c); the running dashboard server was not restarted." + ], + "changes": [ + "awaiting is optional: a status.json from a server older than the column has no key and still renders '📋 N · ⚙ N · ✓ N', never grey.", + "awaiting never changes the colour (it waits on the opener, not kuhy) and stays out of the short text; zero is hidden like '✗'.", + "Still zero forks on the hot path: one more regex field read and an arithmetic test." + ], + "verification": [ + { + "command": "bash linux_configuration/tests/test_i3blocks_issue_dashboard.sh", + "result": "pass", + "evidence": "awaiting / hidden-at-zero / ordering with ✗ / colour / old-format / stale / missing / broken / empty / click asserted; strace still shows one execve and no clone/fork." + }, + { + "command": "bash linux_configuration/tests/test_i3blocks_efficiency.sh", + "result": "pass", + "evidence": "All i3blocks efficiency regression tests passed." + }, + { + "command": "ISSUE_DASHBOARD_STATUS= issue_dashboard.sh", + "result": "pass", + "evidence": "'📋 1 · ⚙ 0 · ✓ 1 · ⏳ 2 · ✗ 1' #FF5555 from {todo 1, ready 1, awaiting 2, failed 1}; the live old-format status.json still renders '📋 14 · ⚙ 0 · ✓ 0' #FFFFFF." + } + ], + "risks": [ + "'⏳' shows only once kuhy restarts the dashboard server; until then the old server keeps writing merged-open issues into todo." + ], + "rollback": [ + "git revert this commit; the block reads the old fields only.", + "Confirm the bar still renders '📋 N · ⚙ N · ✓ N'." + ] +} diff --git a/docs/superpowers/evidence/precommit-workflow-dispatch-2026-08-23.json b/docs/superpowers/evidence/precommit-workflow-dispatch-2026-08-23.json deleted file mode 100644 index b73813f6..00000000 --- a/docs/superpowers/evidence/precommit-workflow-dispatch-2026-08-23.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "intent": "Allow the Pre-commit checks workflow to be re-run manually, so a caching change can be verified on an unchanged tree instead of being asserted.", - "scope": [ - ".github/workflows/pre-commit.yml (triggers only)", - "Non-goal: changing what the job runs" - ], - "changes": ["Added workflow_dispatch to the Pre-commit checks triggers."], - "verification": [ - { - "command": "gh run view 32645759168 --log | grep for cache keys", - "result": "pass", - "evidence": "'Cache not found for input keys: pre-commit-Linux-py3.12-...' then 'Cache saved with key: ...' -- the env cache missed because that commit changed .pre-commit-config.yaml, which is the key. It is populated now." - }, - { - "command": "gh run view 32645759168 --log | grep -c 'Installing environment'", - "result": "pass", - "evidence": "6 hook environments built from scratch, including pylint's with a dozen extra packages -- the bulk of the 126s hook step" - }, - { - "command": "gh run view 32645759168 --log | grep 'pytest with coverage enforcement'", - "result": "pass", - "evidence": "'....Skipped' -- the duplicate-suite skip from the previous commit took effect" - }, - { - "command": "python3 -c \"import yaml; yaml.safe_load(...)\"", - "result": "pass", - "evidence": "workflow parses as valid YAML" - } - ], - "risks": [ - "A dispatch trigger lets anyone with write access run the job on demand; it runs the same read-only checks as a push, so there is no additional privilege." - ], - "rollback": [ - "Remove the workflow_dispatch line.", - "After rollback, confirm the workflow no longer offers a Run workflow button." - ] -} diff --git a/linux_configuration/i3blocks/issue_dashboard.sh b/linux_configuration/i3blocks/issue_dashboard.sh index 51c1fdaf..14000a75 100755 --- a/linux_configuration/i3blocks/issue_dashboard.sh +++ b/linux_configuration/i3blocks/issue_dashboard.sh @@ -2,7 +2,7 @@ # ============================================================================ # issue-dashboard status for i3blocks: issues to do, agents working, work -# ready to merge. +# ready to merge, and merged issues awaiting their opener's close. # # Opened the same way as book_guard.sh: a click runs issue-dashboard-desktop, # which reuses the dashboard server if it is answering (starting it detached @@ -57,6 +57,7 @@ field todo field working field ready field failed +field awaiting field updated_epoch if [[ -z $todo || -z $working || -z $ready || -z $failed || -z $updated_epoch ]]; then @@ -71,6 +72,12 @@ if ((now - updated_epoch > STALE_SECONDS)); then fi text="📋 ${todo} · ⚙ ${working} · ✓ ${ready}" +# Merged, waiting for the opener to close: shown only when there is some, and +# never a colour, since it waits on someone else. Optional because a server +# older than the awaiting column does not write it. +if ((${awaiting:-0} > 0)); then + text="$text · ⏳ ${awaiting}" +fi short="📋 ${todo}" color=$WHITE if ((failed > 0)); then diff --git a/linux_configuration/tests/test_i3blocks_issue_dashboard.sh b/linux_configuration/tests/test_i3blocks_issue_dashboard.sh index af4ce4e5..cfe89eee 100755 --- a/linux_configuration/tests/test_i3blocks_issue_dashboard.sh +++ b/linux_configuration/tests/test_i3blocks_issue_dashboard.sh @@ -39,13 +39,15 @@ chmod +x "$BIN_DIR/launcher" STATUS="$TMP_DIR/status.json" -# status +# status [awaiting] +# With no awaiting the file has no "awaiting" key, as an older server writes. status() { - local epoch + local epoch awaiting='' printf -v epoch '%(%s)T' -1 epoch=$((epoch - $5)) - printf '{"repo": "Sepci0/kado-no-akari", "todo": %s, "working": %s, "ready": %s, "failed": %s, "updated": "x", "updated_epoch": %s}\n' \ - "$1" "$2" "$3" "$4" "$epoch" >"$STATUS" + [[ -n ${6:-} ]] && awaiting=", \"awaiting\": $6" + printf '{"repo": "Sepci0/kado-no-akari", "todo": %s, "working": %s, "ready": %s%s, "failed": %s, "updated": "x", "updated_epoch": %s}\n' \ + "$1" "$2" "$3" "$awaiting" "$4" "$epoch" >"$STATUS" } run_block() { @@ -75,6 +77,22 @@ out=$(run_block) assert_eq '📋 3 · ⚙ 0 · ✓ 2 · ✗ 1' "$(line 1 "$out")" 'failed text' assert_eq '#FF5555' "$(line 3 "$out")" 'a failed agent is red' +printf 'Checking merged issues awaiting their opener...\n' + +status 3 0 0 0 5 2 +out=$(run_block) +assert_eq '📋 3 · ⚙ 0 · ✓ 0 · ⏳ 2' "$(line 1 "$out")" 'awaiting text' +assert_eq '📋 3' "$(line 2 "$out")" 'awaiting stays out of the short text' +assert_eq '#FFFFFF' "$(line 3 "$out")" 'awaiting alone is white' + +status 3 0 0 0 5 0 +assert_eq '📋 3 · ⚙ 0 · ✓ 0' "$(line 1 "$(run_block)")" 'zero awaiting is hidden' + +status 3 0 2 1 5 2 +out=$(run_block) +assert_eq '📋 3 · ⚙ 0 · ✓ 2 · ⏳ 2 · ✗ 1' "$(line 1 "$out")" 'awaiting sits between ready and failed' +assert_eq '#FF5555' "$(line 3 "$out")" 'failed still wins the colour' + printf 'Checking failure states are never blank or stale numbers...\n' status 4 1 2 0 600 @@ -94,7 +112,7 @@ assert_eq '📋 dashboard' "$(line 1 "$(run_block)")" 'empty status' printf 'Checking the hot path forks nothing...\n' -status 4 1 2 0 5 +status 4 1 2 0 5 1 if command -v strace >/dev/null 2>&1; then ISSUE_DASHBOARD_STATUS="$STATUS" strace -f -o "$TMP_DIR/trace" -e trace=execve,clone,clone3,fork,vfork \ bash "$BLOCK" >/dev/null