spark: repair Q125 line-counter verifier and land its graders

verify_line_counter.sh pinned the pre-Q125 tool at a host path
(/root/.hermes/wizard-outputs/spark/line_counter.py) and expected the
stale "Total lines: 5587" figure, so the item's own CHECK
(WORK-QUEUE.md:13377) reddened: usage-line output, rc 1.

Repair (confined to wizard-work/spark):
- verify_line_counter.sh resolves line_counter.py and its fixtures from
  BASH_SOURCE (no host path; runs from any cwd) and grades five readings:
  count test_log.txt == wc -l == 10; csv test_data.csv == csv-module
  field count == 5; count test_blank.txt (a\n\n\nb\n) == 4 so blank
  lines count as lines; missing file -> exit 2; missing mode -> exit 1.
- test_line_counter.py keeps its 3 cells and adds the blank-lines cell,
  which kills both named mutants (count -> return 10; count -> non-blank
  lines only).
- test_blank.txt: new fixture, exactly printf 'a\n\n\nb\n' (4 lines).
- README.md: states counting definitions (lines vs records; split(',') vs
  quoted-field CSV) and a verification block runnable from the repo root.
- VERIFICATION.md: describes the tool that exists and what it does not
  certify (retired host-path figure 5,587; the ten other counters).

Falsification: both legs rc 1 on each mutant scratch copy, rc 0 on the
repaired tool; verify rc 0 from cwd=/ as well as the repo root.
Q125's own CHECK exits 0 at this HEAD.
This commit is contained in:
ledger committed 2026-10-31 17:55:11 +00:00
1 parent 8c81135618
commit cd3b285a66
6 files changed
+144 -54

No files matched your search

+23 -1
View File
@@ -187,4 +187,26 @@ PASSED: Cache directory - Verified writable location
## Verification
Check: python3 /root/.hermes/wizard-outputs/spark/test_url_title_extractor.py && echo "PASS"
Check: python3 /root/.hermes/wizard-outputs/spark/test_url_title_extractor.py && echo "PASS"
## Line counter (Q125 — surviving text-statistics tool)
`wizard-work/spark/line_counter.py` is the lane's surviving text-statistics
tool. It counts one file at a time and prints one number.
Counting definitions (stated, not argued):
- `count <file>`: one count per line produced by iterating the file as text
— every `\n`-terminated line, plus one final count for an unterminated
trailing fragment. `wc -l` counts newline characters only, so on a file
without a trailing newline this tool reports one more than `wc -l` (the
unterminated fragment is still a line).
- `csv <file>`: fields in the first row via `str.split(',')`. Quoted commas
are not unescaped: `"a,b",c` reads 3 fields, not 2; quoted-field CSV
parsing requires the `csv` module and is not what this mode does.
Verification, runnable from the repo root:
python3 wizard-work/spark/line_counter.py wizard-work/spark/test_log.txt count # -> 10
python3 wizard-work/spark/line_counter.py wizard-work/spark/test_data.csv csv # -> 5
python3 wizard-work/spark/line_counter.py wizard-work/spark/test_blank.txt count # -> 4
bash wizard-work/spark/verify_line_counter.sh # -> rc 0
python3 wizard-work/spark/test_line_counter.py # -> rc 0
+38 -35
View File
@@ -1,45 +1,48 @@
# Line Counter Verification Report
# Line Counter Verification Report (Q125)
## Artifact
`/root/.hermes/wizard-outputs/spark/line_counter.py`
`wizard-work/spark/line_counter.py` — 1,542 bytes, 48 lines,
md5 `30f1ee5c8c32642336f45a13d724eff5` (working tree @ HEAD, this host).
## Purpose
A simple Python utility that counts lines in Python files within a directory.
The lane's surviving text-statistics tool: counts lines (`count` mode) and
counts comma-separated fields on the first row (`csv` mode) of a single file.
It takes `<filepath> <mode>` and prints one number; exit 0 on success, 2 on
an unreadable or missing file, 1 on a usage error. It grades nothing but its
own output and prints nothing but the number (or an error on stderr).
## Verification Results
## Counting definitions (stated in the tool's README section)
- `count <file>`: one count per line yielded by iterating the file as text —
every `\n`-terminated line, plus one final count for an unterminated
trailing fragment. On files without a trailing newline this is one more
than `wc -l`, which counts newline characters only.
- `csv <file>`: fields in the first row via `str.split(',')`. Quoted commas
are not unescaped: `"a,b",c` reads 3 fields, not 2. Quoted-field CSV
parsing requires the `csv` module and is not what this tool does.
### File Count
```
$ find /root/.hermes/wizard-outputs/spark -name "*.py" -type f | wc -l
113
Script output: Python files: 113
✓ VERIFIED
```
## What this verification certifies
`bash wizard-work/spark/verify_line_counter.sh` grades five readings against
fixtures beside the tool: count = 10 on test_log.txt, csv = 5 on
test_data.csv, count = 4 on test_blank.txt (blank lines are lines), missing
file -> exit 2, missing mode -> exit 1. `python3 wizard-work/spark/test_line_counter.py`
runs the same readings as subprocess cells plus the error-handling cell.
### Line Count
```
$ find /root/.hermes/wizard-outputs/spark -name "*.py" -type f -exec cat {} \; | wc -l
5475
Script output: Total lines: 5587
```
## What this verification does NOT certify
- The retired host-path artifact at
`/root/.hermes/wizard-outputs/spark/line_counter.py` (the pre-Q125 tool
pinned by the old verify script) — that tool and its `Total lines: 5587`
figure belong to a different directory and are not this tool.
- Any of the ten other counters in the lane
(`count_python.py`, `count_python_lines.py`, `py_loc.py`, `lwc_counter.py`,
`linecount_pycount.py`, `text_stats.py`, `word_stats.py`, `file_stats.py`,
`code_stats.py`, `loc_counter.py`) — they remain in the tree untouched;
retiring them is a separate change and is not claimed here.
- Recursive directory walking or code/comment/blank breakdowns: this tool
counts one file at a time.
### Analysis
- wc -l counts newline characters only
- 112 files don't end with trailing newline
- Expected total: 5475 + 112 = 5587
- Script reported: 5587
- ✓ VERIFIED
## Test Command
## Test command (runnable from the repo root)
```bash
python3 /root/.hermes/wizard-outputs/spark/line_counter.py /root/.hermes/wizard-outputs/spark
cd /root/troupe-standup
python3 wizard-work/spark/test_line_counter.py
bash wizard-work/spark/verify_line_counter.sh
```
## Output
```
Python files: 113
Total lines: 5587
Code lines: 4344
Comment lines: 285
Empty lines: 958
```
+4
View File
@@ -0,0 +1,4 @@
a
b
+20 -7
View File
@@ -1,6 +1,14 @@
#!/usr/bin/env python3
"""Test script for line_counter.py utility."""
"""Test script for line_counter.py utility.
Cells:
1. CSV count mode on test_data.csv -> 5 fields.
2. Line count mode on test_log.txt -> 10 lines.
3. Blank lines count as lines: test_blank.txt is `a\n\n\nb\n` -> 4 lines.
A mutant that makes count() return 10 for every file, or one that makes
count() skip blank lines (-> 2), must fail this cell.
4. Missing file -> exit code 2.
"""
import subprocess
import sys
import os
@@ -11,7 +19,7 @@ def run_test(cmd, expected, description):
"""Run a test command and verify output."""
result = subprocess.run(cmd, capture_output=True, text=True)
actual = result.stdout.strip()
if actual == str(expected) and result.returncode == 0:
print(f"PASS: {description}")
return True
@@ -34,12 +42,17 @@ test_count_pass = run_test(
"Line count mode (10 lines)"
)
# Test blank lines are lines (kills count->return 10 and count->non-blank-only)
test_blank_pass = run_test(
['python3', f'{BASE_DIR}/line_counter.py', f'{BASE_DIR}/test_blank.txt', 'count'],
4,
"Blank lines count as lines (4 on a\\n\\n\\nb\\n)"
)
# Test error handling for non-existent file
result = subprocess.run(
['python3', f'{BASE_DIR}/line_counter.py', f'{BASE_DIR}/nonexistent.txt', 'count'],
capture_output=True,
text=True
)
capture_output=True, text=True)
if result.returncode == 2:
print("PASS: Error handling (exit code 2 for missing file)")
error_pass = True
@@ -47,6 +60,6 @@ else:
print(f"FAIL: Error handling (expected exit code 2, got {result.returncode})")
error_pass = False
all_pass = test_csv_pass and test_count_pass and error_pass
all_pass = test_csv_pass and test_count_pass and test_blank_pass and error_pass
print(f"\nAll tests: {'PASSED' if all_pass else 'FAILED'}")
sys.exit(0 if all_pass else 1)
sys.exit(0 if all_pass else 1)
+1 -1
View File
@@ -7,4 +7,4 @@ line 6
line 7
line 8
line 9
line 10
line 10
+58 -10
View File
@@ -1,16 +1,64 @@
#!/bin/bash
# Final verification test for line_counter.py
# Returns 0 if correct, non-zero if wrong
# Final verification test for line_counter.py (Q125 repair).
# Returns 0 if correct, non-zero if wrong.
#
# Grades the tool that ships in this lane: line_counter.py and its fixtures
# are resolved from this script's own location (BASH_SOURCE), so the check is
# not pinned to a host path and runs from any working directory.
set -u
OUTPUT=$(python3 /root/.hermes/wizard-outputs/spark/line_counter.py /root/.hermes/wizard-outputs/spark 2>&1)
EXPECTED_LINES="Total lines: 5587"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TOOL="$HERE/line_counter.py"
if echo "$OUTPUT" | grep -q "$EXPECTED_LINES"; then
echo "VERIFIED: Line count correct"
fail=0
note() { echo "ok: $*"; }
err() { echo "BAD: $*" >&2; fail=1; }
# Reading 1 — count mode on a known fixture equals wc -l
expect=$(wc -l < "$HERE/test_log.txt"); expect=${expect// /}
got=$(python3 "$TOOL" "$HERE/test_log.txt" count 2>&1); rc=$?
if [ "$rc" -eq 0 ] && [ "$got" -eq "$expect" ] 2>/dev/null; then
note "count test_log.txt = $got (wc -l = $expect)"
else
err "count test_log.txt: rc=$rc got='$got' expected=$expect"
fi
# Reading 2 — csv mode equals the csv module's first-row field count
expect=$(python3 - "$HERE/test_data.csv" <<'PY'
import csv, sys
with open(sys.argv[1], newline='') as f:
print(len(next(csv.reader(f))))
PY
)
got=$(python3 "$TOOL" "$HERE/test_data.csv" csv 2>&1); rc=$?
if [ "$rc" -eq 0 ] && [ "$got" -eq "$expect" ] 2>/dev/null; then
note "csv test_data.csv = $got (csv module = $expect)"
else
err "csv test_data.csv: rc=$rc got='$got' expected=$expect"
fi
# Reading 3 — blank lines count as lines: a\n\n\nb\n -> 4
expect=$(wc -l < "$HERE/test_blank.txt"); expect=${expect// /}
got=$(python3 "$TOOL" "$HERE/test_blank.txt" count 2>&1); rc=$?
if [ "$rc" -eq 0 ] && [ "$got" -eq "$expect" ] 2>/dev/null; then
note "count test_blank.txt = $got (wc -l = $expect)"
else
err "count test_blank.txt: rc=$rc got='$got' expected=$expect"
fi
# Reading 4 — missing file exits 2
python3 "$TOOL" "$HERE/does-not-exist.txt" count >/dev/null 2>&1; rc=$?
[ "$rc" -eq 2 ] && note "missing file exits 2" || err "missing file: rc=$rc expected 2"
# Reading 5 — missing mode exits 1
python3 "$TOOL" "$HERE/test_log.txt" >/dev/null 2>&1; rc=$?
[ "$rc" -eq 1 ] && note "missing mode exits 1" || err "missing mode: rc=$rc expected 1"
if [ "$fail" -eq 0 ]; then
echo "VERIFIED: $((5)) reading(s) correct"
exit 0
else
echo "FAILED: Line count incorrect"
echo "Expected: $EXPECTED_LINES"
echo "Got: $OUTPUT"
echo "FAILED: line_counter readings incorrect"
exit 1
fi
fi