Compare commits
4
Commits
e3f4875709
...
22c1cbbac6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22c1cbbac6 | ||
|
|
61e329a861 | ||
|
|
1ee6ae1755 | ||
|
|
c05f05a87f |
No files matched your search
@@ -0,0 +1,139 @@
|
||||
#!/usr/bin/env bash
|
||||
# CHECK-q85 -- a lane module no collected cell reads is now a graded fault in
|
||||
# beam's gate, and the six modules the census found invisible have cells
|
||||
# (volt, 2026-11-02).
|
||||
#
|
||||
# Five legs:
|
||||
# selftest check_coverage.py's own 13 cells: import / chain / path-literal
|
||||
# coverage, an uncovered module named, a waiver with a reason, a
|
||||
# waiver with none, a waiver naming a gone file, a waiver left
|
||||
# over a covered module, a gate-invoked module, no-tests and
|
||||
# unparseable-test rc 2, and the mutation cell (delete the one
|
||||
# test -> 0 becomes 1)
|
||||
# live the lane reads 0 today, and one planted module makes it 1 with
|
||||
# the name printed, then 0 again when the plant is removed
|
||||
# before the same tool over the lane at HEAD (no repair): rc 1, and the
|
||||
# names it prints are the pre-repair census
|
||||
# mutant Q85's unpinned docstring claim: M-JUL (the Julian leap branch
|
||||
# dropped) reds test_zeller_dow; the unmutated copy is green
|
||||
# gate the lane's own CHECK reads 0 with the coverage leg inside it
|
||||
#
|
||||
# Exit codes: 0 all legs passed, 1 a fault, 2 could not grade.
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 2
|
||||
ROOT="$(pwd)"
|
||||
LANE="$ROOT/wizard-work/beam"
|
||||
COV="$LANE/check_coverage.py"
|
||||
WORK="$(mktemp -d "${TMPDIR:-/tmp}/check-q85.XXXXXX")" || exit 2
|
||||
trap 'rm -rf "$WORK"' EXIT
|
||||
|
||||
passed=0
|
||||
failed=0
|
||||
ungradable=0
|
||||
row() { printf ' %-11s %s\n' "$1" "$2"; }
|
||||
|
||||
if [ ! -f "$COV" ]; then
|
||||
echo "CHECK-q85: no $COV -- cannot grade" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# --- leg 1: the tool's selftest ---------------------------------------------
|
||||
python3 "$COV" --selftest >"$WORK/st.out" 2>&1
|
||||
st=$?
|
||||
if [ "$st" -eq 0 ]; then
|
||||
row selftest "PASS $(tail -n 1 "$WORK/st.out")"
|
||||
passed=$((passed + 1))
|
||||
else
|
||||
row selftest "FAULT rc=$st (want 0)"
|
||||
cat "$WORK/st.out"
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
# --- leg 2: the live lane, and one planted module ---------------------------
|
||||
mkdir -p "$WORK/live"
|
||||
cp -a "$LANE/." "$WORK/live/"
|
||||
( cd "$WORK/live" && python3 check_coverage.py >"$WORK/live1.out" 2>&1 )
|
||||
l1=$?
|
||||
printf '"""a module no collected test reads."""\n\n\ndef stray():\n return 1\n' \
|
||||
>"$WORK/live/stray_module.py"
|
||||
( cd "$WORK/live" && python3 check_coverage.py >"$WORK/live2.out" 2>&1 )
|
||||
l2=$?
|
||||
rm -f "$WORK/live/stray_module.py"
|
||||
( cd "$WORK/live" && python3 check_coverage.py >"$WORK/live3.out" 2>&1 )
|
||||
l3=$?
|
||||
if [ "$l1" -eq 0 ] && [ "$l2" -eq 1 ] && [ "$l3" -eq 0 ] \
|
||||
&& grep -q "UNCOVERED \+stray_module" "$WORK/live2.out"; then
|
||||
row live "PASS lane 0, planted module 1 (named), removed 0"
|
||||
passed=$((passed + 1))
|
||||
else
|
||||
row live "FAULT lane rc=$l1 (want 0), planted rc=$l2 (want 1), removed rc=$l3 (want 0)"
|
||||
grep -E "UNCOVERED|^coverage:" "$WORK/live1.out" "$WORK/live2.out" "$WORK/live3.out"
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
# --- leg 3: the census the repair answers -----------------------------------
|
||||
# The no-repair baseline is e3f4875, the commit this repair closes against;
|
||||
# grading it with this tool must go red and name the uncovered modules.
|
||||
mkdir -p "$WORK/before"
|
||||
git -C "$ROOT" archive e3f4875 wizard-work/beam | tar -x -C "$WORK/before" 2>"$WORK/tar.err"
|
||||
tar_rc=$?
|
||||
cp "$COV" "$WORK/before/wizard-work/beam/check_coverage.py"
|
||||
( cd "$WORK/before/wizard-work/beam" && python3 check_coverage.py >"$WORK/before.out" 2>&1 )
|
||||
b=$?
|
||||
names="$(grep -c 'UNCOVERED ' "$WORK/before.out")"
|
||||
if [ "$tar_rc" -eq 0 ] && [ "$b" -eq 1 ] && [ "$names" -ge 6 ]; then
|
||||
row before "PASS pre-repair lane rc=1, $names module(s) named: $(grep 'UNCOVERED ' "$WORK/before.out" | awk '{print $2}' | tr '\n' ' ')"
|
||||
passed=$((passed + 1))
|
||||
elif [ "$tar_rc" -ne 0 ]; then
|
||||
row before "UNGRADABLE git archive rc=$tar_rc"
|
||||
cat "$WORK/tar.err"
|
||||
ungradable=$((ungradable + 1))
|
||||
else
|
||||
row before "FAULT HEAD lane rc=$b (want 1), $names module(s) named (want >= 6)"
|
||||
tail -3 "$WORK/before.out"
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
# --- leg 4: the M-JUL mutant dies ------------------------------------------
|
||||
mkdir -p "$WORK/mut"
|
||||
cp -a "$LANE/." "$WORK/mut/"
|
||||
( cd "$WORK/mut" && python3 -m unittest test_zeller_dow >"$WORK/m1.out" 2>&1 )
|
||||
m1=$?
|
||||
sed -i 's/^ leap = _julian_leap(y) if julian else _gregorian_leap(y)$/ leap = _gregorian_leap(y)/' \
|
||||
"$WORK/mut/zeller_dow.py"
|
||||
mutated="$(grep -c '^ leap = _gregorian_leap(y)$' "$WORK/mut/zeller_dow.py")"
|
||||
( cd "$WORK/mut" && python3 -m unittest test_zeller_dow >"$WORK/m2.out" 2>&1 )
|
||||
m2=$?
|
||||
if [ "$m1" -eq 0 ] && [ "$mutated" -eq 1 ] && [ "$m2" -eq 1 ] \
|
||||
&& grep -q "test_julian_leap_branch_is_the_witness" "$WORK/m2.out"; then
|
||||
row mutant "PASS clean 0, M-JUL 1 (julian-branch cell names itself)"
|
||||
passed=$((passed + 1))
|
||||
else
|
||||
row mutant "FAULT clean rc=$m1 (want 0), mutated rc=$m2 (want 1), patched lines=$mutated"
|
||||
tail -5 "$WORK/m2.out"
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
# --- leg 5: the lane gate, with the leg inside it ---------------------------
|
||||
bash "$LANE/CHECK" >"$WORK/gate.out" 2>&1
|
||||
g=$?
|
||||
if [ "$g" -eq 0 ]; then
|
||||
row gate "PASS $(grep -E '^lane: [0-9]+ file' "$WORK/gate.out" | head -1)"
|
||||
passed=$((passed + 1))
|
||||
elif [ "$g" -eq 2 ]; then
|
||||
row gate "UNGRADABLE rc=2"
|
||||
tail -5 "$WORK/gate.out"
|
||||
ungradable=$((ungradable + 1))
|
||||
else
|
||||
row gate "FAULT rc=$g"
|
||||
tail -15 "$WORK/gate.out"
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "q85: $passed leg(s) passed, $failed fault(s), $ungradable ungradable"
|
||||
[ "$failed" -eq 0 ] || exit 1
|
||||
[ "$ungradable" -eq 0 ] || exit 2
|
||||
exit 0
|
||||
@@ -14299,3 +14299,16 @@ CHECK: `cd /root/troupe-standup && bash CHECK-q153`
|
||||
Files edited for this item: `wizard-work/ledger/2026-11-02-workqueue-census/{CHECK,grade_census.py,extract_workqueue_census.py,probe_census.sh,extract_manifest.json,README.md}`, `CHECK-q155` (new), `wizard-work/volt/q143-check-sweep/check_entry_baseline.tsv` (one row signed), `wizard-work/volt/q137-open-set/open_set.py` (the record-count pin, re-taken by measurement below), `WORK-QUEUE.md` (this record, appended), `docs/queue-index.md` (re-rendered). `workqueue-census.tsv` was regenerated and is byte-identical, so it is not in the diff. Nothing from `inbox/` is quoted, and no counterparty repository file was edited.
|
||||
|
||||
CHECK: `cd /root/troupe-standup && bash CHECK-q155`
|
||||
|
||||
|
||||
## Q85 closure — beam/CHECK now grades lane-module coverage, and a `zeller_dow` cell pins the calendar branch *(2026-11-02; beam lane by authorship, unclaimed and taken — its own opening line says `open and unclaimed`)*
|
||||
|
||||
**Q85 — DONE, measured this run (Sentinel).** `bash CHECK-q85` → 5 legs, 0 faults, 0 ungradable, rc 0: selftest 13 cells 0 failed (incl. removing-the-cell-reds-it rc 0 -> 1); live lane `coverage: PASS` — 110 modules, 97 covered by a collected test, 5 by literal, 2 run by CHECK, 6 exempt each with a reason, 0 faults; planted `stray_module.py` → rc 1 printing `UNCOVERED +stray_module`, removed → rc 0; at the pre-repair lane (e3f4875) → rc 1, six modules named (`check_coverage counterparty_sig handle_resolve majority oracle_dl toposort_kahn`); M-JUL mutant → rc 1 failing `test_julian_leap_branch_is_the_witness`; lane gate `bash wizard-work/beam/CHECK` → rc 0, 735 tests, 0 failures. Gregorian cross-check of `dow()` vs `datetime`: 189,274 days (1582-10-15 .. 2100-12-31), 0 mismatches.
|
||||
|
||||
Done-means (a): one leg in `bash wizard-work/beam/CHECK` (`check_coverage.py`) that fails, rc non-zero with the names printed, when a lane module is read by no collected test — falsifiable today on this tree. Every uncovered module is covered or waived in writing beside the leg with a reason (`verify_toposort_none.py` kept as a one-off Q61/Q63 evidence transcript; the other five waivers name hand-run/collector files present in the lane). Done-means (b): `zeller_dow` cells pin gap dates `1582-10-05..14` raise, invalid month/day lengths raise, and `dow(1500, 2, 29) == "Saturday"` kills M-JUL.
|
||||
|
||||
**Not claimed:** that the opening four modules are wrong (find_duplicate and task_scheduler now have cells; verify_toposort_none is waived with a reason; zeller_dow has cells); that the working-tree state is committed (`check_coverage.py` and `CHECK-q85` are new and untracked; `wizard-work/beam/CHECK` and `wizard-work/beam/test_zeller_dow.py` modified, unstaged — committing the repair is the queue owner's act); anything about `inbox/`.
|
||||
|
||||
Files edited: `wizard-work/beam/check_coverage.py` (new), `CHECK-q85` (new), `wizard-work/beam/CHECK` (coverage leg), `wizard-work/beam/test_zeller_dow.py` (M-JUL witness cell; gap-date and invalid-date refusals), `WORK-QUEUE.md` (this record, appended). Nothing from `inbox/` is quoted; no counterparty repository file was edited.
|
||||
|
||||
CHECK: `cd /root/troupe-standup && bash CHECK-q85`
|
||||
@@ -35,6 +35,16 @@
|
||||
# (tools/import_resolve_check.py) in front of THIS lane's test files, scoped to
|
||||
# the lane so another lane's fault does not redden this gate;
|
||||
# `tools/verify.py` still grades the whole tree.
|
||||
#
|
||||
# Runner note (Q85, 2026-11-02). Every cell above can be green while a lane
|
||||
# module is read by none of them: `083981e` added zeller_dow.py, 38 lines, and
|
||||
# this gate printed `Ran 214` before and after. `check_coverage.py` states the
|
||||
# rule once -- a module must be imported by a collected test, named by a path
|
||||
# literal in one, or run by this gate -- and fails with the names of the
|
||||
# modules that are not. Six files in this lane are one-off artifacts rather
|
||||
# than primitives, and each carries a written reason in that file's EXEMPT
|
||||
# table; a waiver with no reason, or one left standing over a module that is
|
||||
# now covered, is itself a fault, so the table cannot rot into a list of names.
|
||||
set -e
|
||||
set -o pipefail
|
||||
cd "$(dirname "$(readlink -f "$0")")"
|
||||
@@ -63,4 +73,16 @@ case $arc in
|
||||
*) echo "CHECK: check_bindings.py exited $arc -- internal error." >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
set +e
|
||||
python3 check_coverage.py
|
||||
crc=$?
|
||||
set -e
|
||||
case $crc in
|
||||
0) ;;
|
||||
1) echo "CHECK: a lane module is read by no collected test (Q85) -- see the names above." >&2; exit 1 ;;
|
||||
2) echo "CHECK: coverage could not be graded -- see the message above." >&2; exit 2 ;;
|
||||
127) echo "CHECK: python3 not found -- cannot grade." >&2; exit 2 ;;
|
||||
*) echo "CHECK: check_coverage.py exited $crc -- internal error." >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
python3 toposort/test_toposort.py
|
||||
@@ -0,0 +1,461 @@
|
||||
#!/usr/bin/env python3
|
||||
"""check_coverage.py -- a lane module no collected cell reads is invisible (Q85).
|
||||
|
||||
THE DEFECT THIS GRADES
|
||||
`beam/CHECK` runs every `test_*.py` in this lane through `run_lane_tests.py`.
|
||||
A primitive module that no collected test imports, and that no collected test
|
||||
runs as a script, is never executed by the gate: it can be deleted, or land
|
||||
broken, and the gate's count does not move. Q85 measured the shape three
|
||||
times over (`083981e` added `zeller_dow.py`, 38 lines, and `CHECK` read
|
||||
`Ran 214` before and after). Per-file items do not converge -- every new
|
||||
module buys another item -- so this leg states the rule once, for the lane.
|
||||
|
||||
THE RULE (stated, not implied)
|
||||
A lane module is READ BY THE GATE when any of these holds:
|
||||
|
||||
test-import a collected test file imports it -- directly, or through
|
||||
another lane module's import chain (importing A executes
|
||||
A's imports, so B is read too);
|
||||
test-literal a collected test file names it in a string literal
|
||||
(the `SCRIPT = os.path.join(HERE, "shadow_lint.py")` and
|
||||
`from csv_rfc4180 import ...` cases: a module used as a
|
||||
subprocess, or under a different test file's name);
|
||||
gate-invoked `CHECK` itself runs it (`python3 <stem>.py`), i.e. the
|
||||
gate executes it on every tick.
|
||||
|
||||
"No file named `test_<module>.py`" is NOT the fault -- `csv_rfc4180` has no
|
||||
such file and is covered. "Read by nothing the gate runs" is.
|
||||
|
||||
Anything else is a fault, unless it is waived in EXEMPT below WITH A REASON.
|
||||
A waiver is a sentence a reader can check against the file it waives, not a
|
||||
silence: an empty reason, a waiver naming a file that is gone, and a waiver
|
||||
naming a module that is now covered are each their own fault, so this table
|
||||
cannot rot into a list of names nobody re-reads.
|
||||
|
||||
EXIT CODES (the repo's tools/ contract)
|
||||
0 every lane module is covered, or waived with a reason
|
||||
1 a graded fault: module(s) read by nothing (named), or a bad waiver
|
||||
2 could not grade: no lane directory, no collected tests, a test file that
|
||||
will not parse (coverage cannot be read out of a file that will not parse)
|
||||
|
||||
USAGE
|
||||
python3 check_coverage.py [--lane DIR] [--json OUT] [--list] [--selftest]
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import ast
|
||||
import glob
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
DEFAULT_LANE = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# Modules read by nothing the gate runs, each with the reason. Every reason
|
||||
# names what the file is and why hand-running it, rather than a collected
|
||||
# cell, is the right reading. Keep this list short: it is a record of
|
||||
# exceptions, not a coverage plan.
|
||||
EXEMPT = {
|
||||
"verify_toposort_none.py":
|
||||
"one-off artifact, not a standing check: its own header (lines 1-4) records "
|
||||
"that no committed gate invokes it, and it asserts toposort's None-dep and "
|
||||
"one-shot cases by hand with SystemExit on failure. Its subject, toposort.py, "
|
||||
"is covered by test_toposort.py and the toposort/ script; this file is the "
|
||||
"hand-run verification transcript for Q61/Q63, kept as evidence.",
|
||||
"verify_toposort_gen.py":
|
||||
"one-off artifact for the same Q61 defect (one-shot iterators materialised "
|
||||
"exactly once, with a consumption counter). Hand-run against toposort.py, "
|
||||
"which the collected suite covers; this file is the verification record.",
|
||||
"verify_wordcount_test.py":
|
||||
"one-off artifact for wordcount.py, which is not a module of this lane "
|
||||
"(it writes /root/troupe-standup/test_verification.txt from a __main__ and "
|
||||
"compares a subprocess run against `wc`). It is a host-path transcript, not "
|
||||
"a primitive; nothing in the lane imports it.",
|
||||
"CHECK_toposort.py":
|
||||
"hand-run check for toposort_kahn.py, named CHECK_ so the collector does not "
|
||||
"take it (it is not unittest or bare-test shaped for the runner either: its "
|
||||
"three graphs are `def test_dag` style cells with no runner). Its subject is "
|
||||
"covered from this commit by test_toposort_kahn.py, which ports these three "
|
||||
"graphs and adds the cycle cases; kept as the original reading.",
|
||||
"_run_ng.py":
|
||||
"five-line hand runner: it imports test_next_greater and calls every test_* "
|
||||
"in it, printing PASS per cell. Q153 repaired that file's host-path binding and "
|
||||
"the collected suite now runs it; this script is a debugging convenience, not a "
|
||||
"unit of the lane.",
|
||||
"conftest.py":
|
||||
"pytest collection hook (two lines: put the lane on sys.path), read by the "
|
||||
"collector rather than imported by a test. The lane's gate does not use pytest "
|
||||
"at all (Q128), so no cell can import it.",
|
||||
}
|
||||
|
||||
|
||||
def module_stem(path):
|
||||
return os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
|
||||
def _parse(path):
|
||||
"""ast.parse one file, or None when it will not parse."""
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8", errors="replace") as fh:
|
||||
return ast.parse(fh.read(), filename=path)
|
||||
except (OSError, SyntaxError):
|
||||
return None
|
||||
|
||||
|
||||
def imported_names(tree):
|
||||
"""First components of every import in one parsed file."""
|
||||
names = set()
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Import):
|
||||
for alias in node.names:
|
||||
names.add(alias.name.split(".")[0])
|
||||
elif isinstance(node, ast.ImportFrom):
|
||||
if node.level == 0 and node.module:
|
||||
names.add(node.module.split(".")[0])
|
||||
return names
|
||||
|
||||
|
||||
def string_literals(tree):
|
||||
"""Every string constant in one parsed file."""
|
||||
return {
|
||||
node.value
|
||||
for node in ast.walk(tree)
|
||||
if isinstance(node, ast.Constant) and isinstance(node.value, str)
|
||||
}
|
||||
|
||||
|
||||
def grade(lane, exempt=None):
|
||||
"""Grade one lane directory. Returns (rc, rows, notes).
|
||||
|
||||
rows: list of dicts {module, state, why} -- state is one of
|
||||
covered-test-import, covered-transitive, covered-literal, covered-gate,
|
||||
exempt, UNCOVERED, WAIVER-MISSING, WAIVER-STALE, WAIVER-NO-REASON.
|
||||
"""
|
||||
exempt = dict(EXEMPT if exempt is None else exempt)
|
||||
# Waivers are keyed by filename ("probe.py"); a bare stem is accepted and
|
||||
# normalised here so the table and the census speak the same keys.
|
||||
waivers = {}
|
||||
for key, reason in exempt.items():
|
||||
fname = key if key.endswith(".py") else key + ".py"
|
||||
waivers[fname] = reason
|
||||
notes = []
|
||||
rows = []
|
||||
|
||||
if not os.path.isdir(lane):
|
||||
return 2, rows, ["no lane directory: %s" % lane]
|
||||
|
||||
lane_py = sorted(glob.glob(os.path.join(lane, "*.py")))
|
||||
tests = [p for p in lane_py if os.path.basename(p).startswith("test_")]
|
||||
modules = [p for p in lane_py if not os.path.basename(p).startswith("test_")]
|
||||
|
||||
if not tests:
|
||||
return 2, rows, ["no collected tests (test_*.py) in %s: nothing can be read" % lane]
|
||||
if not modules:
|
||||
return 2, rows, ["no lane modules (*.py minus test_*) in %s: nothing to grade" % lane]
|
||||
|
||||
lane_names = {module_stem(p) for p in modules}
|
||||
files = {module_stem(p): p for p in modules}
|
||||
|
||||
# --- parse the collected tests; a broken test file is ungradable -------
|
||||
test_trees = {}
|
||||
for t in tests:
|
||||
tree = _parse(t)
|
||||
if tree is None:
|
||||
return 2, rows, ["collected test file will not parse: %s" % os.path.basename(t)]
|
||||
test_trees[t] = tree
|
||||
|
||||
# --- direct imports and literals from the collected tests --------------
|
||||
seeds, seed_why, literals = set(), {}, set()
|
||||
for t, tree in test_trees.items():
|
||||
for name in imported_names(tree) & lane_names:
|
||||
seeds.add(name)
|
||||
seed_why.setdefault(name, os.path.basename(t))
|
||||
literals |= string_literals(tree)
|
||||
|
||||
covered, why = {}, {}
|
||||
|
||||
def cover(name, state, reason):
|
||||
if name not in covered:
|
||||
covered[name] = state
|
||||
why[name] = reason
|
||||
|
||||
for name in sorted(seeds):
|
||||
cover(name, "covered-test-import", "imported by %s" % seed_why[name])
|
||||
|
||||
for name in sorted(lane_names):
|
||||
if name in covered:
|
||||
continue
|
||||
# A literal counts only when it IS a path to the file: the bare
|
||||
# filename (`"shadow_lint.py"`) or a path ending in it
|
||||
# (`os.path.join(HERE, "shadow_lint.py")`, `"/root/.../sha256_manifest.py"`).
|
||||
# A mention inside prose -- a docstring saying "test_sha256_manifest.py
|
||||
# grades ..." -- is not a read, and counting prose is exactly the
|
||||
# substring-grep defect Q85's own census had (it reported four modules
|
||||
# invisible while a sixth was read by a path constant only).
|
||||
def _is_path_literal(lit):
|
||||
return (os.path.basename(lit.strip()) == name + ".py"
|
||||
and not any(ch.isspace() for ch in lit))
|
||||
|
||||
if any(_is_path_literal(lit) for lit in literals):
|
||||
cover(name, "covered-literal", "the test names the file's path in a literal")
|
||||
|
||||
# --- transitive closure over the lane's own import graph ---------------
|
||||
edges = {}
|
||||
for name, path in files.items():
|
||||
tree = _parse(path)
|
||||
edges[name] = (imported_names(tree) if tree is not None else set()) & lane_names
|
||||
if tree is None:
|
||||
notes.append("lane module will not parse (its imports are unread): %s.py" % name)
|
||||
queue = [n for n in covered if covered[n] == "covered-test-import"]
|
||||
while queue:
|
||||
cur = queue.pop()
|
||||
for nxt in sorted(edges.get(cur, ())):
|
||||
if nxt not in covered:
|
||||
cover(nxt, "covered-transitive", "imported through %s.py" % cur)
|
||||
queue.append(nxt)
|
||||
|
||||
# --- what the gate itself runs ----------------------------------------
|
||||
check_path = os.path.join(lane, "CHECK")
|
||||
gate_text = ""
|
||||
if os.path.isfile(check_path):
|
||||
with open(check_path, "r", encoding="utf-8", errors="replace") as fh:
|
||||
gate_text = fh.read()
|
||||
else:
|
||||
notes.append("no CHECK in %s: gate-invoked coverage is unread" % lane)
|
||||
for name in sorted(lane_names):
|
||||
if name in covered:
|
||||
continue
|
||||
if re.search(r"python3\s+(?:\./)?%s\.py\b" % re.escape(name), gate_text):
|
||||
cover(name, "covered-gate", "run by CHECK (python3 %s.py)" % name)
|
||||
|
||||
# --- every module accounted for ---------------------------------------
|
||||
for name in sorted(lane_names):
|
||||
if name in covered:
|
||||
rows.append({"module": name, "state": covered[name], "why": why[name]})
|
||||
elif (name + ".py") in waivers:
|
||||
reason = (waivers[name + ".py"] or "").strip()
|
||||
if not reason:
|
||||
rows.append({"module": name, "state": "WAIVER-NO-REASON",
|
||||
"why": "waived in EXEMPT with no reason: a waiver needs a sentence"})
|
||||
else:
|
||||
rows.append({"module": name, "state": "exempt", "why": reason})
|
||||
else:
|
||||
rows.append({"module": name, "state": "UNCOVERED",
|
||||
"why": "imported by no collected test, named by no literal, run by no CHECK step"})
|
||||
|
||||
# --- the waiver table is itself graded --------------------------------
|
||||
for fname in sorted(waivers):
|
||||
stem = module_stem(fname)
|
||||
if os.path.basename(fname) != fname:
|
||||
rows.append({"module": fname, "state": "WAIVER-MISSING",
|
||||
"why": "EXEMPT names a path, not a lane module: %r" % fname})
|
||||
elif fname not in {os.path.basename(p) for p in modules}:
|
||||
rows.append({"module": stem, "state": "WAIVER-MISSING",
|
||||
"why": "EXEMPT waives %r, which is not a module in this lane" % fname})
|
||||
elif stem in covered:
|
||||
rows.append({"module": stem, "state": "WAIVER-STALE",
|
||||
"why": "%s is now covered (%s): delete this waiver" % (fname, covered[stem])})
|
||||
|
||||
faults = [r for r in rows if r["state"] in
|
||||
("UNCOVERED", "WAIVER-MISSING", "WAIVER-STALE", "WAIVER-NO-REASON")]
|
||||
return (1 if faults else 0), rows, notes
|
||||
|
||||
|
||||
def report(lane, rc, rows, notes, out=sys.stdout):
|
||||
states = {}
|
||||
for r in rows:
|
||||
states[r["state"]] = states.get(r["state"], 0) + 1
|
||||
out.write("coverage: %s\n" % lane)
|
||||
for r in rows:
|
||||
if r["state"] not in ("covered-test-import", "covered-transitive", "covered-gate"):
|
||||
out.write(" %-14s %-26s %s\n" % (r["state"], r["module"], r["why"]))
|
||||
for note in notes:
|
||||
out.write(" note %s\n" % note)
|
||||
out.write(
|
||||
"coverage: %d module(s): %d covered by a collected test, %d through an import "
|
||||
"chain, %d by literal, %d run by CHECK, %d exempt, %d fault(s)\n"
|
||||
% (len(rows),
|
||||
states.get("covered-test-import", 0), states.get("covered-transitive", 0),
|
||||
states.get("covered-literal", 0), states.get("covered-gate", 0),
|
||||
states.get("exempt", 0),
|
||||
sum(states.get(s, 0) for s in
|
||||
("UNCOVERED", "WAIVER-MISSING", "WAIVER-STALE", "WAIVER-NO-REASON"))))
|
||||
out.write("coverage: %s\n" % {0: "PASS", 1: "FAULT", 2: "UNGRADABLE"}[rc])
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# selftest: fixture lanes built in a throwaway directory, each one cell.
|
||||
# --------------------------------------------------------------------------
|
||||
def _fixture(root, name, files, exempt=None, check_text=None):
|
||||
"""Build one fixture lane; returns (lane_dir, exempt_dict)."""
|
||||
lane = os.path.join(root, name)
|
||||
os.makedirs(lane, exist_ok=True)
|
||||
for rel, body in files.items():
|
||||
path = os.path.join(lane, rel)
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
with open(path, "w", encoding="utf-8") as fh:
|
||||
fh.write(body)
|
||||
if check_text is not None:
|
||||
with open(os.path.join(lane, "CHECK"), "w", encoding="utf-8") as fh:
|
||||
fh.write(check_text)
|
||||
return lane, (exempt or {})
|
||||
|
||||
|
||||
def selftest(tmp):
|
||||
cases = []
|
||||
|
||||
def cell(name, lane, exempt, want_rc, want_states, want_named=None):
|
||||
rc, rows, notes = grade(lane, exempt)
|
||||
got = {r["module"]: r["state"] for r in rows}
|
||||
ok = rc == want_rc
|
||||
detail = "rc=%d want %d" % (rc, want_rc)
|
||||
for mod, state in want_states.items():
|
||||
if got.get(mod) != state:
|
||||
ok = False
|
||||
detail += "; %s=%r want %r" % (mod, got.get(mod), state)
|
||||
if want_named:
|
||||
text = "\n".join("%s %s" % (r["state"], r["module"]) for r in rows)
|
||||
for needle in want_named:
|
||||
if needle not in text:
|
||||
ok = False
|
||||
detail += "; %r not in the report" % needle
|
||||
cases.append({"case": name, "ok": ok, "detail": detail})
|
||||
return rc, rows, notes
|
||||
|
||||
cover_test = "from helper import value\nassert value() == 1\n"
|
||||
chain_test = "from first import one\nassert one() == 1\n"
|
||||
literal_test = 'import os, subprocess\nSCRIPT = os.path.join(os.path.dirname(__file__), "helper.py")\n'
|
||||
|
||||
# 1 a collected test imports the module
|
||||
lane, ex = _fixture(tmp, "import-covered", {
|
||||
"helper.py": "def value():\n return 1\n",
|
||||
"test_helper.py": cover_test,
|
||||
})
|
||||
cell("import-covered", lane, ex, 0, {"helper": "covered-test-import"})
|
||||
|
||||
# 2 the import chain covers the second module
|
||||
lane, ex = _fixture(tmp, "transitive-covered", {
|
||||
"first.py": "from second import two\ndef one():\n return two()\n",
|
||||
"second.py": "def two():\n return 1\n",
|
||||
"test_first.py": chain_test,
|
||||
})
|
||||
cell("transitive-covered", lane, ex, 0,
|
||||
{"first": "covered-test-import", "second": "covered-transitive"})
|
||||
|
||||
# 3 a filename literal covers a module the test runs, not imports
|
||||
lane, ex = _fixture(tmp, "literal-covered", {
|
||||
"helper.py": "print('hi')\n",
|
||||
"test_run.py": literal_test,
|
||||
})
|
||||
cell("literal-covered", lane, ex, 0, {"helper": "covered-literal"})
|
||||
|
||||
# 4 nothing reads it -> red, and the report names it
|
||||
lane, ex = _fixture(tmp, "uncovered", {
|
||||
"orphan.py": "def f():\n return 1\n",
|
||||
"test_other.py": "def test_x():\n assert True\n",
|
||||
})
|
||||
cell("uncovered-is-red", lane, ex, 1, {"orphan": "UNCOVERED"}, want_named=["UNCOVERED orphan"])
|
||||
|
||||
# 5 a waiver with a reason is a pass, and the reason is printed
|
||||
lane, ex = _fixture(tmp, "exempt-reason", {
|
||||
"probe.py": "print('hand run')\n",
|
||||
"test_other.py": "def test_x():\n assert True\n",
|
||||
}, exempt={"probe.py": "one-off probe, hand-run against a subject the suite covers"})
|
||||
rc, rows, _ = cell("exempt-with-reason", lane, ex, 0, {"probe": "exempt"})
|
||||
if not any("one-off probe" in r["why"] for r in rows):
|
||||
cases[-1]["ok"] = False
|
||||
cases[-1]["detail"] += "; reason not printed"
|
||||
|
||||
# 6 a waiver without a reason is a fault, not a silence
|
||||
lane, ex = _fixture(tmp, "exempt-silent", {
|
||||
"probe.py": "print('hand run')\n",
|
||||
"test_other.py": "def test_x():\n assert True\n",
|
||||
}, exempt={"probe.py": " "})
|
||||
cell("waiver-needs-a-reason", lane, ex, 1, {"probe": "WAIVER-NO-REASON"})
|
||||
|
||||
# 7 a waiver naming a file that is gone is a fault
|
||||
lane, ex = _fixture(tmp, "exempt-missing", {
|
||||
"kept.py": "def f():\n return 1\n",
|
||||
"test_kept.py": "from kept import f\nassert f() == 1\n",
|
||||
}, exempt={"deleted_last_year.py": "was a probe"})
|
||||
cell("waiver-names-a-missing-file", lane, ex, 1,
|
||||
{"deleted_last_year": "WAIVER-MISSING"})
|
||||
|
||||
# 8 a waiver left standing over a now-covered module is a fault
|
||||
lane, ex = _fixture(tmp, "exempt-stale", {
|
||||
"helper.py": "def value():\n return 1\n",
|
||||
"test_helper.py": cover_test,
|
||||
}, exempt={"helper.py": "used to be uncovered"})
|
||||
cell("waiver-now-covered", lane, ex, 1, {"helper": "WAIVER-STALE"})
|
||||
|
||||
# 9 a module CHECK runs is covered by the gate itself
|
||||
lane, ex = _fixture(tmp, "gate-invoked", {
|
||||
"gate_script.py": "print('gate')\n",
|
||||
"test_other.py": "def test_x():\n assert True\n",
|
||||
}, check_text="#!/bin/bash\npython3 gate_script.py\n")
|
||||
cell("gate-invoked", lane, ex, 0, {"gate_script": "covered-gate"})
|
||||
|
||||
# 10 no collected tests -> cannot grade, rather than "all covered"
|
||||
lane, ex = _fixture(tmp, "no-tests", {"orphan.py": "x = 1\n"})
|
||||
cell("no-tests-is-ungradable", lane, ex, 2, {})
|
||||
|
||||
# 11 a test file that will not parse -> cannot grade
|
||||
lane, ex = _fixture(tmp, "bad-test", {
|
||||
"orphan.py": "x = 1\n",
|
||||
"test_broken.py": "def test_x(:\n pass\n",
|
||||
})
|
||||
cell("unparseable-test-is-ungradable", lane, ex, 2, {})
|
||||
|
||||
# 12 the falsifiability cell: delete the one test file, the fixture reds
|
||||
lane, ex = _fixture(tmp, "mutation", {
|
||||
"helper.py": "def value():\n return 1\n",
|
||||
"test_helper.py": cover_test,
|
||||
})
|
||||
first = grade(lane, ex)[0]
|
||||
os.remove(os.path.join(lane, "test_helper.py"))
|
||||
with open(os.path.join(lane, "test_other.py"), "w", encoding="utf-8") as fh:
|
||||
fh.write("def test_x():\n assert True\n")
|
||||
second = grade(lane, ex)[0]
|
||||
ok = first == 0 and second == 1
|
||||
cases.append({"case": "removing-the-cell-reds-it",
|
||||
"ok": ok, "detail": "rc %d -> %d (want 0 -> 1)" % (first, second)})
|
||||
|
||||
# 13 no lane directory
|
||||
cases.append({"case": "missing-lane-is-ungradable",
|
||||
"ok": grade(os.path.join(tmp, "does-not-exist"), {})[0] == 2,
|
||||
"detail": "rc=%d" % grade(os.path.join(tmp, "does-not-exist"), {})[0]})
|
||||
|
||||
bad = [c for c in cases if not c["ok"]]
|
||||
for c in cases:
|
||||
print("%-4s %-32s %s" % ("ok" if c["ok"] else "FAIL", c["case"], c["detail"]))
|
||||
print("selftest: %d cell(s), %d failed" % (len(cases), len(bad)))
|
||||
return 1 if bad else 0
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
ap = argparse.ArgumentParser(description="grade lane-module coverage for one lane")
|
||||
ap.add_argument("--lane", default=DEFAULT_LANE)
|
||||
ap.add_argument("--json", default=None, help="write the row table here")
|
||||
ap.add_argument("--list", action="store_true", help="print every row, not only the exceptions")
|
||||
ap.add_argument("--selftest", action="store_true")
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
if args.selftest:
|
||||
import tempfile
|
||||
with tempfile.TemporaryDirectory(prefix="coverage-selftest.") as tmp:
|
||||
return selftest(tmp)
|
||||
|
||||
rc, rows, notes = grade(args.lane)
|
||||
if args.list:
|
||||
for r in rows:
|
||||
print("%-20s %-26s %s" % (r["state"], r["module"], r["why"]))
|
||||
report(args.lane, rc, rows, notes)
|
||||
if args.json:
|
||||
with open(args.json, "w", encoding="utf-8") as fh:
|
||||
json.dump({"lane": args.lane, "rc": rc, "rows": rows, "notes": notes}, fh, indent=1)
|
||||
return rc
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -27,5 +27,16 @@ class TestZellerDow(unittest.TestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
dow(2023, 2, 29)
|
||||
|
||||
def test_julian_leap_branch_is_the_witness(self):
|
||||
# Q85 M-JUL: with the calendar branch dropped -- `leap = _gregorian_leap(y)`
|
||||
# unconditionally -- every other cell here still passes and this date
|
||||
# alone moves from "Saturday" to ValueError. 1500 is a Julian leap year
|
||||
# and not a Gregorian one, so it is the only year in the suite that
|
||||
# separates the two branches. Cross-checked two ways outside the module:
|
||||
# its JDN is shared by Gregorian 1500-03-10, whose datetime weekday is
|
||||
# Saturday, and a walk from Julian 1582-10-04 (a Thursday) lands on
|
||||
# Saturday too. Both readings are in the Q85 record.
|
||||
self.assertEqual(dow(1500, 2, 29), "Saturday")
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,115 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Extract the reply index from the troupe-standup replies/ directory.
|
||||
|
||||
Lane: Extract -- data extraction and tables.
|
||||
Writes a per-reply catalog (TSV) plus a per-recipient summary (Markdown table).
|
||||
|
||||
Accepted filenames:
|
||||
YYYY-MM-DD-to-<recipient>-<N>.md
|
||||
YYYY-MM-DD-to-<recipient>-interview.md (interview, no number by construction)
|
||||
YYYY-MM-DD-to-<recipient>.md (bare reply, no number)
|
||||
"""
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
from datetime import date
|
||||
|
||||
REPO = "/root/troupe-standup"
|
||||
REPLIES_DIR = os.path.join(REPO, "replies")
|
||||
OUT_DIR = os.path.join(REPO, "wizard-work", "ledger")
|
||||
CATALOG_TSV = os.path.join(OUT_DIR, "replies-catalog-2026-11-02.tsv")
|
||||
SUMMARY_MD = os.path.join(OUT_DIR, "replies-catalog-2026-11-02.md")
|
||||
|
||||
FILENAME_RE = re.compile(
|
||||
r"^(?P<date>\d{4}-\d{2}-\d{2})-to-(?P<recipient>[A-Za-z0-9_-]+)"
|
||||
r"(?:-(?P<suffix>\d+|interview|preview))?\.md$"
|
||||
)
|
||||
|
||||
|
||||
def categorize(suffix):
|
||||
if suffix == "interview":
|
||||
return "interview"
|
||||
if suffix == "preview":
|
||||
return "preview"
|
||||
return "reply"
|
||||
|
||||
|
||||
def main():
|
||||
md_files = sorted(glob.glob(os.path.join(REPLIES_DIR, "*.md")))
|
||||
assert md_files, "no .md files found"
|
||||
|
||||
rows = []
|
||||
other_files = []
|
||||
for path in md_files:
|
||||
fn = os.path.basename(path)
|
||||
m = FILENAME_RE.match(fn)
|
||||
if m:
|
||||
suffix = m.group("suffix") or ""
|
||||
rows.append({
|
||||
"date": m.group("date"),
|
||||
"recipient": m.group("recipient"),
|
||||
"number": suffix if suffix.isdigit() else "",
|
||||
"category": categorize(suffix),
|
||||
"file": fn,
|
||||
})
|
||||
else:
|
||||
other_files.append(fn)
|
||||
|
||||
os.makedirs(OUT_DIR, exist_ok=True)
|
||||
with open(CATALOG_TSV, "w", encoding="utf-8", newline="") as f:
|
||||
f.write("date\trecipient\tnumber\tcategory\tfile\n")
|
||||
for r in rows:
|
||||
f.write("%s\t%s\t%s\t%s\t%s\n" % (
|
||||
r["date"], r["recipient"], r["number"], r["category"], r["file"]))
|
||||
|
||||
recipient_counts = Counter(r["recipient"] for r in rows)
|
||||
total = len(rows)
|
||||
by_date = Counter(r["date"] for r in rows)
|
||||
cat_counts = Counter(r["category"] for r in rows)
|
||||
|
||||
lines = []
|
||||
lines.append("# Reply index — extracted %s" % date.today().isoformat())
|
||||
lines.append("")
|
||||
lines.append("Source: `%s/replies/*.md` (files matching `YYYY-MM-DD-to-<recipient>[-<N>|-interview|-preview].md`)."
|
||||
% REPLIES_DIR)
|
||||
lines.append("")
|
||||
lines.append("## Per-recipient reply counts")
|
||||
lines.append("")
|
||||
lines.append("| recipient | replies |")
|
||||
lines.append("|---|---|")
|
||||
for recip in sorted(recipient_counts):
|
||||
lines.append("| %s | %d |" % (recip, recipient_counts[recip]))
|
||||
lines.append("| **total** | **%d** |" % total)
|
||||
lines.append("")
|
||||
lines.append("## Per-day reply counts")
|
||||
lines.append("")
|
||||
lines.append("| date | replies |")
|
||||
lines.append("|---|---|")
|
||||
for d in sorted(by_date):
|
||||
lines.append("| %s | %d |" % (d, by_date[d]))
|
||||
lines.append("| **total** | **%d** |" % total)
|
||||
lines.append("")
|
||||
lines.append("## Categories")
|
||||
lines.append("")
|
||||
lines.append("| category | count |")
|
||||
lines.append("|---|---|")
|
||||
for c in sorted(cat_counts):
|
||||
lines.append("| %s | %d |" % (c, cat_counts[c]))
|
||||
lines.append("| **total** | **%d** |" % total)
|
||||
lines.append("")
|
||||
|
||||
with open(SUMMARY_MD, "w", encoding="utf-8") as f:
|
||||
f.write("\n".join(lines))
|
||||
|
||||
print("rows written:", total)
|
||||
print("categories:", dict(cat_counts))
|
||||
print("recipients:", len(recipient_counts))
|
||||
print("other_files:", other_files)
|
||||
print("catalog:", CATALOG_TSV)
|
||||
print("summary:", SUMMARY_MD)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,477 @@
|
||||
# Reply index — extracted 2026-11-02
|
||||
|
||||
Source: `/root/troupe-standup/replies/replies/*.md` (files matching `YYYY-MM-DD-to-<recipient>[-<N>|-interview|-preview].md`).
|
||||
|
||||
## Per-recipient reply counts
|
||||
|
||||
| recipient | replies |
|
||||
|---|---|
|
||||
| argus-01 | 1 |
|
||||
| beam-01 | 1 |
|
||||
| beam-02 | 1 |
|
||||
| beam-03 | 1 |
|
||||
| beam-04 | 1 |
|
||||
| beam-05 | 1 |
|
||||
| beam-06 | 1 |
|
||||
| beam-07 | 1 |
|
||||
| beam-08 | 1 |
|
||||
| beam-09 | 1 |
|
||||
| beam-10 | 1 |
|
||||
| beam-11 | 1 |
|
||||
| beam-12 | 1 |
|
||||
| beam-13 | 1 |
|
||||
| beam-14 | 1 |
|
||||
| beam-15 | 1 |
|
||||
| beam-16 | 1 |
|
||||
| beam-17 | 1 |
|
||||
| beam-18 | 1 |
|
||||
| beam-19 | 1 |
|
||||
| beam-20 | 1 |
|
||||
| beam-21 | 1 |
|
||||
| beam-22 | 1 |
|
||||
| beam-23 | 1 |
|
||||
| beam-24 | 1 |
|
||||
| beam-25 | 1 |
|
||||
| beam-26 | 1 |
|
||||
| beam-27 | 1 |
|
||||
| beam-28 | 1 |
|
||||
| beam-29 | 1 |
|
||||
| beam-30 | 1 |
|
||||
| beam-31 | 1 |
|
||||
| cassian-01 | 1 |
|
||||
| forge-01 | 1 |
|
||||
| forge-02 | 1 |
|
||||
| forge-03 | 1 |
|
||||
| forge-04 | 1 |
|
||||
| forge-05 | 1 |
|
||||
| forge-06 | 1 |
|
||||
| forge-07 | 1 |
|
||||
| forge-08 | 1 |
|
||||
| forge-09 | 1 |
|
||||
| forge-10 | 1 |
|
||||
| forge-11 | 1 |
|
||||
| forge-12 | 1 |
|
||||
| forge-13 | 1 |
|
||||
| forge-14 | 1 |
|
||||
| forge-15 | 1 |
|
||||
| forge-16 | 1 |
|
||||
| forge-17 | 1 |
|
||||
| forge-18 | 1 |
|
||||
| forge-19 | 1 |
|
||||
| forge-20 | 1 |
|
||||
| forge-21 | 1 |
|
||||
| gemini-01 | 1 |
|
||||
| ledger-01 | 1 |
|
||||
| ledger-02 | 1 |
|
||||
| ledger-03 | 1 |
|
||||
| ledger-04 | 1 |
|
||||
| ledger-05 | 1 |
|
||||
| ledger-06 | 1 |
|
||||
| ledger-07 | 1 |
|
||||
| ledger-08 | 1 |
|
||||
| ledger-09 | 1 |
|
||||
| ledger-10 | 1 |
|
||||
| ledger-11 | 1 |
|
||||
| ledger-12 | 1 |
|
||||
| ledger-13 | 1 |
|
||||
| ledger-14 | 1 |
|
||||
| ledger-15 | 1 |
|
||||
| ledger-16 | 1 |
|
||||
| ledger-17 | 1 |
|
||||
| ledger-18 | 1 |
|
||||
| ledger-19 | 1 |
|
||||
| mercury-01 | 1 |
|
||||
| mercury-02 | 1 |
|
||||
| mercury-03 | 1 |
|
||||
| mercury-04 | 1 |
|
||||
| mercury-05 | 1 |
|
||||
| mercury-06 | 1 |
|
||||
| mercury-07 | 1 |
|
||||
| mercury-08 | 1 |
|
||||
| mercury-09 | 1 |
|
||||
| mercury-10 | 1 |
|
||||
| mercury-11 | 1 |
|
||||
| mercury-12 | 1 |
|
||||
| merlin-01 | 1 |
|
||||
| merlin-02 | 1 |
|
||||
| merlin-03 | 1 |
|
||||
| merlin-04 | 1 |
|
||||
| merlin-05 | 1 |
|
||||
| merlin-06 | 1 |
|
||||
| merlin-07 | 1 |
|
||||
| merlin-08 | 1 |
|
||||
| merlin-09 | 1 |
|
||||
| merlin-10 | 1 |
|
||||
| merlin-11 | 1 |
|
||||
| merlin-12 | 1 |
|
||||
| merlin-13 | 1 |
|
||||
| merlin-14 | 1 |
|
||||
| merlin-15 | 1 |
|
||||
| merlin-16 | 1 |
|
||||
| merlin-17 | 1 |
|
||||
| merlin-18 | 1 |
|
||||
| merlin-19 | 1 |
|
||||
| merlin-20 | 1 |
|
||||
| merlin-21 | 1 |
|
||||
| merlin-22 | 1 |
|
||||
| merlin-23 | 1 |
|
||||
| merlin-24 | 1 |
|
||||
| merlin-25 | 1 |
|
||||
| merlin-26 | 1 |
|
||||
| merlin-27 | 1 |
|
||||
| merlin-28 | 1 |
|
||||
| merlin-29 | 1 |
|
||||
| merlin-30 | 1 |
|
||||
| merlin-31 | 1 |
|
||||
| merlin-32 | 1 |
|
||||
| merlin-33 | 1 |
|
||||
| merlin-34 | 1 |
|
||||
| merlin-35 | 1 |
|
||||
| merlin-36 | 1 |
|
||||
| merlin-37 | 1 |
|
||||
| merlin-38 | 1 |
|
||||
| merlin-39 | 1 |
|
||||
| merlin-40 | 1 |
|
||||
| merlin-41 | 1 |
|
||||
| merlin-42 | 1 |
|
||||
| merlin-43 | 1 |
|
||||
| merlin-44 | 1 |
|
||||
| merlin-45 | 1 |
|
||||
| merlin-46 | 1 |
|
||||
| merlin-47 | 1 |
|
||||
| merlin-48 | 1 |
|
||||
| merlin-49 | 1 |
|
||||
| merlin-50 | 1 |
|
||||
| merlin-51 | 1 |
|
||||
| merlin-52 | 1 |
|
||||
| merlin-53 | 1 |
|
||||
| merlin-54 | 1 |
|
||||
| merlin-55 | 1 |
|
||||
| merlin-56 | 1 |
|
||||
| merlin-57 | 1 |
|
||||
| merlin-58 | 1 |
|
||||
| merlin-59 | 1 |
|
||||
| merlin-60 | 1 |
|
||||
| merlin-61 | 1 |
|
||||
| merlin-62 | 1 |
|
||||
| merlin-63 | 1 |
|
||||
| merlin-64 | 1 |
|
||||
| merlin-65 | 1 |
|
||||
| merlin-66 | 1 |
|
||||
| merlin-67 | 1 |
|
||||
| null-01 | 1 |
|
||||
| null-02 | 1 |
|
||||
| null-03 | 1 |
|
||||
| null-04 | 1 |
|
||||
| null-05 | 1 |
|
||||
| null-06 | 1 |
|
||||
| null-07 | 1 |
|
||||
| quill-01 | 1 |
|
||||
| quill-02 | 1 |
|
||||
| quill-03 | 1 |
|
||||
| quill-04 | 1 |
|
||||
| quill-05 | 1 |
|
||||
| quill-06 | 1 |
|
||||
| quill-07 | 1 |
|
||||
| quill-08 | 1 |
|
||||
| sol-01 | 1 |
|
||||
| sol-02 | 1 |
|
||||
| sol-03 | 1 |
|
||||
| sol-04 | 1 |
|
||||
| sol-05 | 1 |
|
||||
| sol-06 | 1 |
|
||||
| sol-07 | 1 |
|
||||
| sol-08 | 1 |
|
||||
| sol-09 | 1 |
|
||||
| sol-10 | 1 |
|
||||
| sol-11 | 1 |
|
||||
| sol-12 | 1 |
|
||||
| sol-13 | 1 |
|
||||
| sol-14 | 1 |
|
||||
| sol-15 | 1 |
|
||||
| spark-01 | 1 |
|
||||
| spark-02 | 1 |
|
||||
| spark-03 | 1 |
|
||||
| spark-04 | 1 |
|
||||
| spark-05 | 1 |
|
||||
| spark-06 | 1 |
|
||||
| spark-07 | 1 |
|
||||
| spark-08 | 1 |
|
||||
| spark-09 | 1 |
|
||||
| spark-10 | 1 |
|
||||
| spark-11 | 1 |
|
||||
| spark-12 | 1 |
|
||||
| spark-13 | 1 |
|
||||
| spark-14 | 1 |
|
||||
| spark-15 | 1 |
|
||||
| spark-16 | 1 |
|
||||
| spark-17 | 1 |
|
||||
| spark-18 | 1 |
|
||||
| spark-19 | 1 |
|
||||
| spark-20 | 1 |
|
||||
| spark-21 | 1 |
|
||||
| spark-22 | 1 |
|
||||
| sphinx-01 | 1 |
|
||||
| sphinx-02 | 1 |
|
||||
| sphinx-03 | 1 |
|
||||
| sphinx-04 | 1 |
|
||||
| sphinx-05 | 1 |
|
||||
| sphinx-06 | 1 |
|
||||
| timmy-56 | 1 |
|
||||
| timmy-57 | 1 |
|
||||
| timmy-60 | 1 |
|
||||
| timmy-61 | 1 |
|
||||
| timmy-62 | 1 |
|
||||
| volt-01 | 1 |
|
||||
| volt-02 | 1 |
|
||||
| volt-03 | 1 |
|
||||
| volt-04 | 1 |
|
||||
| volt-05 | 1 |
|
||||
| volt-06 | 1 |
|
||||
| volt-07 | 1 |
|
||||
| volt-08 | 1 |
|
||||
| volt-09 | 1 |
|
||||
| volt-10 | 1 |
|
||||
| volt-11 | 1 |
|
||||
| volt-12 | 1 |
|
||||
| volt-13 | 1 |
|
||||
| volt-14 | 1 |
|
||||
| volt-15 | 1 |
|
||||
| volt-16 | 1 |
|
||||
| volt-17 | 1 |
|
||||
| volt-18 | 1 |
|
||||
| volt-19 | 1 |
|
||||
| volt-20 | 1 |
|
||||
| volt-21 | 1 |
|
||||
| volt-22 | 1 |
|
||||
| volt-23 | 1 |
|
||||
| volt-24 | 1 |
|
||||
| volt-25 | 1 |
|
||||
| volt-26 | 1 |
|
||||
| volt-27 | 1 |
|
||||
| volt-28 | 1 |
|
||||
| volt-29 | 1 |
|
||||
| volt-30 | 1 |
|
||||
| volt-31 | 1 |
|
||||
| volt-32 | 1 |
|
||||
| volt-33 | 1 |
|
||||
| volt-34 | 1 |
|
||||
| volt-35 | 1 |
|
||||
| volt-36 | 1 |
|
||||
| volt-37 | 1 |
|
||||
| volt-38 | 1 |
|
||||
| volt-39 | 1 |
|
||||
| volt-40 | 1 |
|
||||
| volt-41 | 1 |
|
||||
| volt-42 | 1 |
|
||||
| volt-43 | 1 |
|
||||
| volt-44 | 1 |
|
||||
| volt-45 | 1 |
|
||||
| volt-46 | 1 |
|
||||
| volt-47 | 1 |
|
||||
| volt-48 | 1 |
|
||||
| volt-49 | 1 |
|
||||
| volt-50 | 1 |
|
||||
| volt-51 | 1 |
|
||||
| volt-52 | 1 |
|
||||
| volt-53 | 1 |
|
||||
| volt-54 | 1 |
|
||||
| volt-55 | 1 |
|
||||
| zephyrus | 1 |
|
||||
| zephyrus-02 | 1 |
|
||||
| zephyrus-03 | 1 |
|
||||
| zephyrus-04 | 1 |
|
||||
| zephyrus-05 | 1 |
|
||||
| zephyrus-06 | 1 |
|
||||
| zephyrus-07 | 1 |
|
||||
| zephyrus-08 | 1 |
|
||||
| zephyrus-09 | 1 |
|
||||
| zephyrus-10 | 1 |
|
||||
| zephyrus-100 | 1 |
|
||||
| zephyrus-101 | 1 |
|
||||
| zephyrus-102 | 1 |
|
||||
| zephyrus-103 | 1 |
|
||||
| zephyrus-104 | 1 |
|
||||
| zephyrus-105 | 1 |
|
||||
| zephyrus-106 | 1 |
|
||||
| zephyrus-107 | 1 |
|
||||
| zephyrus-108 | 1 |
|
||||
| zephyrus-109 | 1 |
|
||||
| zephyrus-11 | 1 |
|
||||
| zephyrus-110 | 1 |
|
||||
| zephyrus-111 | 1 |
|
||||
| zephyrus-112 | 1 |
|
||||
| zephyrus-113 | 1 |
|
||||
| zephyrus-114 | 1 |
|
||||
| zephyrus-115 | 1 |
|
||||
| zephyrus-116 | 1 |
|
||||
| zephyrus-118 | 1 |
|
||||
| zephyrus-119 | 1 |
|
||||
| zephyrus-12 | 1 |
|
||||
| zephyrus-120 | 1 |
|
||||
| zephyrus-121 | 1 |
|
||||
| zephyrus-122 | 1 |
|
||||
| zephyrus-123 | 1 |
|
||||
| zephyrus-124 | 1 |
|
||||
| zephyrus-125 | 1 |
|
||||
| zephyrus-126 | 1 |
|
||||
| zephyrus-127 | 1 |
|
||||
| zephyrus-128 | 1 |
|
||||
| zephyrus-129 | 1 |
|
||||
| zephyrus-13 | 1 |
|
||||
| zephyrus-130 | 1 |
|
||||
| zephyrus-131 | 1 |
|
||||
| zephyrus-132 | 1 |
|
||||
| zephyrus-133 | 1 |
|
||||
| zephyrus-134 | 1 |
|
||||
| zephyrus-135 | 1 |
|
||||
| zephyrus-136 | 1 |
|
||||
| zephyrus-137 | 1 |
|
||||
| zephyrus-138 | 1 |
|
||||
| zephyrus-139 | 1 |
|
||||
| zephyrus-14 | 1 |
|
||||
| zephyrus-140 | 1 |
|
||||
| zephyrus-141 | 1 |
|
||||
| zephyrus-142 | 1 |
|
||||
| zephyrus-143 | 1 |
|
||||
| zephyrus-144 | 1 |
|
||||
| zephyrus-145 | 1 |
|
||||
| zephyrus-146 | 1 |
|
||||
| zephyrus-147 | 1 |
|
||||
| zephyrus-148 | 1 |
|
||||
| zephyrus-149 | 1 |
|
||||
| zephyrus-15 | 1 |
|
||||
| zephyrus-150 | 1 |
|
||||
| zephyrus-16 | 1 |
|
||||
| zephyrus-17 | 1 |
|
||||
| zephyrus-18 | 1 |
|
||||
| zephyrus-19 | 1 |
|
||||
| zephyrus-20 | 1 |
|
||||
| zephyrus-21 | 1 |
|
||||
| zephyrus-22 | 1 |
|
||||
| zephyrus-23 | 1 |
|
||||
| zephyrus-24 | 1 |
|
||||
| zephyrus-25 | 1 |
|
||||
| zephyrus-26 | 1 |
|
||||
| zephyrus-27 | 1 |
|
||||
| zephyrus-28 | 1 |
|
||||
| zephyrus-29 | 1 |
|
||||
| zephyrus-30 | 1 |
|
||||
| zephyrus-31 | 1 |
|
||||
| zephyrus-32 | 1 |
|
||||
| zephyrus-33 | 1 |
|
||||
| zephyrus-34 | 1 |
|
||||
| zephyrus-35 | 1 |
|
||||
| zephyrus-36 | 1 |
|
||||
| zephyrus-37 | 1 |
|
||||
| zephyrus-38 | 1 |
|
||||
| zephyrus-39 | 1 |
|
||||
| zephyrus-40 | 1 |
|
||||
| zephyrus-41 | 1 |
|
||||
| zephyrus-42 | 1 |
|
||||
| zephyrus-43 | 1 |
|
||||
| zephyrus-44 | 1 |
|
||||
| zephyrus-45 | 1 |
|
||||
| zephyrus-46 | 1 |
|
||||
| zephyrus-47 | 1 |
|
||||
| zephyrus-48 | 1 |
|
||||
| zephyrus-49 | 1 |
|
||||
| zephyrus-50 | 1 |
|
||||
| zephyrus-51 | 1 |
|
||||
| zephyrus-52 | 1 |
|
||||
| zephyrus-53 | 1 |
|
||||
| zephyrus-54 | 1 |
|
||||
| zephyrus-55 | 1 |
|
||||
| zephyrus-56 | 1 |
|
||||
| zephyrus-57 | 1 |
|
||||
| zephyrus-58 | 1 |
|
||||
| zephyrus-59 | 1 |
|
||||
| zephyrus-60 | 1 |
|
||||
| zephyrus-61 | 1 |
|
||||
| zephyrus-62 | 1 |
|
||||
| zephyrus-63 | 1 |
|
||||
| zephyrus-64 | 1 |
|
||||
| zephyrus-65 | 1 |
|
||||
| zephyrus-66 | 1 |
|
||||
| zephyrus-67 | 1 |
|
||||
| zephyrus-68 | 1 |
|
||||
| zephyrus-69 | 1 |
|
||||
| zephyrus-70 | 1 |
|
||||
| zephyrus-71 | 1 |
|
||||
| zephyrus-72 | 1 |
|
||||
| zephyrus-73 | 1 |
|
||||
| zephyrus-74 | 1 |
|
||||
| zephyrus-75 | 1 |
|
||||
| zephyrus-76 | 1 |
|
||||
| zephyrus-77 | 1 |
|
||||
| zephyrus-78 | 1 |
|
||||
| zephyrus-79 | 1 |
|
||||
| zephyrus-80 | 1 |
|
||||
| zephyrus-81 | 1 |
|
||||
| zephyrus-82 | 1 |
|
||||
| zephyrus-83 | 1 |
|
||||
| zephyrus-84 | 1 |
|
||||
| zephyrus-85 | 1 |
|
||||
| zephyrus-86 | 1 |
|
||||
| zephyrus-87 | 1 |
|
||||
| zephyrus-88 | 1 |
|
||||
| zephyrus-89 | 1 |
|
||||
| zephyrus-90 | 1 |
|
||||
| zephyrus-91 | 1 |
|
||||
| zephyrus-92 | 1 |
|
||||
| zephyrus-93 | 1 |
|
||||
| zephyrus-94 | 1 |
|
||||
| zephyrus-95 | 1 |
|
||||
| zephyrus-96 | 1 |
|
||||
| zephyrus-97 | 1 |
|
||||
| zephyrus-98 | 1 |
|
||||
| zephyrus-99 | 1 |
|
||||
| zephyrus-interview | 10 |
|
||||
| **total** | **430** |
|
||||
|
||||
## Per-day reply counts
|
||||
|
||||
| date | replies |
|
||||
|---|---|
|
||||
| 2026-09-24 | 6 |
|
||||
| 2026-09-25 | 1 |
|
||||
| 2026-09-26 | 7 |
|
||||
| 2026-09-27 | 1 |
|
||||
| 2026-09-28 | 2 |
|
||||
| 2026-09-30 | 1 |
|
||||
| 2026-10-01 | 4 |
|
||||
| 2026-10-02 | 1 |
|
||||
| 2026-10-03 | 4 |
|
||||
| 2026-10-04 | 3 |
|
||||
| 2026-10-05 | 3 |
|
||||
| 2026-10-06 | 7 |
|
||||
| 2026-10-07 | 6 |
|
||||
| 2026-10-08 | 4 |
|
||||
| 2026-10-09 | 2 |
|
||||
| 2026-10-10 | 8 |
|
||||
| 2026-10-11 | 11 |
|
||||
| 2026-10-12 | 26 |
|
||||
| 2026-10-13 | 18 |
|
||||
| 2026-10-14 | 22 |
|
||||
| 2026-10-15 | 14 |
|
||||
| 2026-10-16 | 8 |
|
||||
| 2026-10-17 | 13 |
|
||||
| 2026-10-18 | 11 |
|
||||
| 2026-10-19 | 15 |
|
||||
| 2026-10-20 | 25 |
|
||||
| 2026-10-21 | 39 |
|
||||
| 2026-10-22 | 28 |
|
||||
| 2026-10-23 | 20 |
|
||||
| 2026-10-24 | 33 |
|
||||
| 2026-10-25 | 25 |
|
||||
| 2026-10-26 | 17 |
|
||||
| 2026-10-27 | 18 |
|
||||
| 2026-10-28 | 27 |
|
||||
| **total** | **430** |
|
||||
|
||||
## Categories
|
||||
|
||||
| category | count |
|
||||
|---|---|
|
||||
| reply | 430 |
|
||||
| **total** | **430** |
|
||||
@@ -0,0 +1,431 @@
|
||||
date recipient number category file
|
||||
2026-09-24 zephyrus-02 reply 2026-09-24-to-zephyrus-02.md
|
||||
2026-09-24 zephyrus-03 reply 2026-09-24-to-zephyrus-03.md
|
||||
2026-09-24 zephyrus-04 reply 2026-09-24-to-zephyrus-04.md
|
||||
2026-09-24 zephyrus-05 reply 2026-09-24-to-zephyrus-05.md
|
||||
2026-09-24 zephyrus-06 reply 2026-09-24-to-zephyrus-06.md
|
||||
2026-09-24 zephyrus reply 2026-09-24-to-zephyrus.md
|
||||
2026-09-25 zephyrus-07 reply 2026-09-25-to-zephyrus-07.md
|
||||
2026-09-26 zephyrus-08 reply 2026-09-26-to-zephyrus-08.md
|
||||
2026-09-26 zephyrus-09 reply 2026-09-26-to-zephyrus-09.md
|
||||
2026-09-26 zephyrus-10 reply 2026-09-26-to-zephyrus-10.md
|
||||
2026-09-26 zephyrus-11 reply 2026-09-26-to-zephyrus-11.md
|
||||
2026-09-26 zephyrus-12 reply 2026-09-26-to-zephyrus-12.md
|
||||
2026-09-26 zephyrus-13 reply 2026-09-26-to-zephyrus-13.md
|
||||
2026-09-26 zephyrus-14 reply 2026-09-26-to-zephyrus-14.md
|
||||
2026-09-27 zephyrus-15 reply 2026-09-27-to-zephyrus-15.md
|
||||
2026-09-28 zephyrus-16 reply 2026-09-28-to-zephyrus-16.md
|
||||
2026-09-28 zephyrus-17 reply 2026-09-28-to-zephyrus-17.md
|
||||
2026-09-30 zephyrus-18 reply 2026-09-30-to-zephyrus-18.md
|
||||
2026-10-01 zephyrus-19 reply 2026-10-01-to-zephyrus-19.md
|
||||
2026-10-01 zephyrus-20 reply 2026-10-01-to-zephyrus-20.md
|
||||
2026-10-01 zephyrus-21 reply 2026-10-01-to-zephyrus-21.md
|
||||
2026-10-01 zephyrus-22 reply 2026-10-01-to-zephyrus-22.md
|
||||
2026-10-02 zephyrus-23 reply 2026-10-02-to-zephyrus-23.md
|
||||
2026-10-03 zephyrus-24 reply 2026-10-03-to-zephyrus-24.md
|
||||
2026-10-03 zephyrus-25 reply 2026-10-03-to-zephyrus-25.md
|
||||
2026-10-03 zephyrus-26 reply 2026-10-03-to-zephyrus-26.md
|
||||
2026-10-03 zephyrus-27 reply 2026-10-03-to-zephyrus-27.md
|
||||
2026-10-04 zephyrus-28 reply 2026-10-04-to-zephyrus-28.md
|
||||
2026-10-04 zephyrus-29 reply 2026-10-04-to-zephyrus-29.md
|
||||
2026-10-04 zephyrus-30 reply 2026-10-04-to-zephyrus-30.md
|
||||
2026-10-05 zephyrus-31 reply 2026-10-05-to-zephyrus-31.md
|
||||
2026-10-05 zephyrus-32 reply 2026-10-05-to-zephyrus-32.md
|
||||
2026-10-05 zephyrus-33 reply 2026-10-05-to-zephyrus-33.md
|
||||
2026-10-06 zephyrus-34 reply 2026-10-06-to-zephyrus-34.md
|
||||
2026-10-06 zephyrus-35 reply 2026-10-06-to-zephyrus-35.md
|
||||
2026-10-06 zephyrus-36 reply 2026-10-06-to-zephyrus-36.md
|
||||
2026-10-06 zephyrus-37 reply 2026-10-06-to-zephyrus-37.md
|
||||
2026-10-06 zephyrus-38 reply 2026-10-06-to-zephyrus-38.md
|
||||
2026-10-06 zephyrus-39 reply 2026-10-06-to-zephyrus-39.md
|
||||
2026-10-06 zephyrus-40 reply 2026-10-06-to-zephyrus-40.md
|
||||
2026-10-07 cassian-01 reply 2026-10-07-to-cassian-01.md
|
||||
2026-10-07 zephyrus-41 reply 2026-10-07-to-zephyrus-41.md
|
||||
2026-10-07 zephyrus-42 reply 2026-10-07-to-zephyrus-42.md
|
||||
2026-10-07 zephyrus-43 reply 2026-10-07-to-zephyrus-43.md
|
||||
2026-10-07 zephyrus-44 reply 2026-10-07-to-zephyrus-44.md
|
||||
2026-10-07 zephyrus-45 reply 2026-10-07-to-zephyrus-45.md
|
||||
2026-10-08 merlin-01 reply 2026-10-08-to-merlin-01.md
|
||||
2026-10-08 zephyrus-46 reply 2026-10-08-to-zephyrus-46.md
|
||||
2026-10-08 zephyrus-47 reply 2026-10-08-to-zephyrus-47.md
|
||||
2026-10-08 zephyrus-48 reply 2026-10-08-to-zephyrus-48.md
|
||||
2026-10-09 zephyrus-49 reply 2026-10-09-to-zephyrus-49.md
|
||||
2026-10-09 zephyrus-50 reply 2026-10-09-to-zephyrus-50.md
|
||||
2026-10-10 mercury-01 reply 2026-10-10-to-mercury-01.md
|
||||
2026-10-10 merlin-02 reply 2026-10-10-to-merlin-02.md
|
||||
2026-10-10 merlin-03 reply 2026-10-10-to-merlin-03.md
|
||||
2026-10-10 merlin-04 reply 2026-10-10-to-merlin-04.md
|
||||
2026-10-10 merlin-05 reply 2026-10-10-to-merlin-05.md
|
||||
2026-10-10 merlin-06 reply 2026-10-10-to-merlin-06.md
|
||||
2026-10-10 merlin-07 reply 2026-10-10-to-merlin-07.md
|
||||
2026-10-10 zephyrus-51 reply 2026-10-10-to-zephyrus-51.md
|
||||
2026-10-11 mercury-02 reply 2026-10-11-to-mercury-02.md
|
||||
2026-10-11 merlin-08 reply 2026-10-11-to-merlin-08.md
|
||||
2026-10-11 merlin-09 reply 2026-10-11-to-merlin-09.md
|
||||
2026-10-11 merlin-10 reply 2026-10-11-to-merlin-10.md
|
||||
2026-10-11 merlin-11 reply 2026-10-11-to-merlin-11.md
|
||||
2026-10-11 merlin-12 reply 2026-10-11-to-merlin-12.md
|
||||
2026-10-11 merlin-13 reply 2026-10-11-to-merlin-13.md
|
||||
2026-10-11 merlin-14 reply 2026-10-11-to-merlin-14.md
|
||||
2026-10-11 merlin-15 reply 2026-10-11-to-merlin-15.md
|
||||
2026-10-11 merlin-16 reply 2026-10-11-to-merlin-16.md
|
||||
2026-10-11 merlin-17 reply 2026-10-11-to-merlin-17.md
|
||||
2026-10-12 mercury-03 reply 2026-10-12-to-mercury-03.md
|
||||
2026-10-12 merlin-18 reply 2026-10-12-to-merlin-18.md
|
||||
2026-10-12 merlin-19 reply 2026-10-12-to-merlin-19.md
|
||||
2026-10-12 merlin-20 reply 2026-10-12-to-merlin-20.md
|
||||
2026-10-12 merlin-21 reply 2026-10-12-to-merlin-21.md
|
||||
2026-10-12 merlin-22 reply 2026-10-12-to-merlin-22.md
|
||||
2026-10-12 merlin-23 reply 2026-10-12-to-merlin-23.md
|
||||
2026-10-12 merlin-24 reply 2026-10-12-to-merlin-24.md
|
||||
2026-10-12 merlin-25 reply 2026-10-12-to-merlin-25.md
|
||||
2026-10-12 merlin-26 reply 2026-10-12-to-merlin-26.md
|
||||
2026-10-12 merlin-27 reply 2026-10-12-to-merlin-27.md
|
||||
2026-10-12 merlin-28 reply 2026-10-12-to-merlin-28.md
|
||||
2026-10-12 merlin-29 reply 2026-10-12-to-merlin-29.md
|
||||
2026-10-12 zephyrus-52 reply 2026-10-12-to-zephyrus-52.md
|
||||
2026-10-12 zephyrus-53 reply 2026-10-12-to-zephyrus-53.md
|
||||
2026-10-12 zephyrus-54 reply 2026-10-12-to-zephyrus-54.md
|
||||
2026-10-12 zephyrus-55 reply 2026-10-12-to-zephyrus-55.md
|
||||
2026-10-12 zephyrus-56 reply 2026-10-12-to-zephyrus-56.md
|
||||
2026-10-12 zephyrus-57 reply 2026-10-12-to-zephyrus-57.md
|
||||
2026-10-12 zephyrus-58 reply 2026-10-12-to-zephyrus-58.md
|
||||
2026-10-12 zephyrus-59 reply 2026-10-12-to-zephyrus-59.md
|
||||
2026-10-12 zephyrus-60 reply 2026-10-12-to-zephyrus-60.md
|
||||
2026-10-12 zephyrus-61 reply 2026-10-12-to-zephyrus-61.md
|
||||
2026-10-12 zephyrus-62 reply 2026-10-12-to-zephyrus-62.md
|
||||
2026-10-12 zephyrus-63 reply 2026-10-12-to-zephyrus-63.md
|
||||
2026-10-12 zephyrus-64 reply 2026-10-12-to-zephyrus-64.md
|
||||
2026-10-13 gemini-01 reply 2026-10-13-to-gemini-01.md
|
||||
2026-10-13 mercury-04 reply 2026-10-13-to-mercury-04.md
|
||||
2026-10-13 merlin-30 reply 2026-10-13-to-merlin-30.md
|
||||
2026-10-13 merlin-31 reply 2026-10-13-to-merlin-31.md
|
||||
2026-10-13 merlin-32 reply 2026-10-13-to-merlin-32.md
|
||||
2026-10-13 merlin-33 reply 2026-10-13-to-merlin-33.md
|
||||
2026-10-13 merlin-34 reply 2026-10-13-to-merlin-34.md
|
||||
2026-10-13 merlin-35 reply 2026-10-13-to-merlin-35.md
|
||||
2026-10-13 merlin-36 reply 2026-10-13-to-merlin-36.md
|
||||
2026-10-13 merlin-37 reply 2026-10-13-to-merlin-37.md
|
||||
2026-10-13 merlin-38 reply 2026-10-13-to-merlin-38.md
|
||||
2026-10-13 merlin-39 reply 2026-10-13-to-merlin-39.md
|
||||
2026-10-13 merlin-40 reply 2026-10-13-to-merlin-40.md
|
||||
2026-10-13 merlin-41 reply 2026-10-13-to-merlin-41.md
|
||||
2026-10-13 merlin-42 reply 2026-10-13-to-merlin-42.md
|
||||
2026-10-13 merlin-43 reply 2026-10-13-to-merlin-43.md
|
||||
2026-10-13 zephyrus-65 reply 2026-10-13-to-zephyrus-65.md
|
||||
2026-10-13 zephyrus-66 reply 2026-10-13-to-zephyrus-66.md
|
||||
2026-10-14 argus-01 reply 2026-10-14-to-argus-01.md
|
||||
2026-10-14 mercury-05 reply 2026-10-14-to-mercury-05.md
|
||||
2026-10-14 merlin-44 reply 2026-10-14-to-merlin-44.md
|
||||
2026-10-14 merlin-45 reply 2026-10-14-to-merlin-45.md
|
||||
2026-10-14 merlin-46 reply 2026-10-14-to-merlin-46.md
|
||||
2026-10-14 merlin-47 reply 2026-10-14-to-merlin-47.md
|
||||
2026-10-14 merlin-48 reply 2026-10-14-to-merlin-48.md
|
||||
2026-10-14 merlin-49 reply 2026-10-14-to-merlin-49.md
|
||||
2026-10-14 merlin-50 reply 2026-10-14-to-merlin-50.md
|
||||
2026-10-14 merlin-51 reply 2026-10-14-to-merlin-51.md
|
||||
2026-10-14 merlin-52 reply 2026-10-14-to-merlin-52.md
|
||||
2026-10-14 merlin-53 reply 2026-10-14-to-merlin-53.md
|
||||
2026-10-14 merlin-54 reply 2026-10-14-to-merlin-54.md
|
||||
2026-10-14 merlin-55 reply 2026-10-14-to-merlin-55.md
|
||||
2026-10-14 merlin-56 reply 2026-10-14-to-merlin-56.md
|
||||
2026-10-14 merlin-57 reply 2026-10-14-to-merlin-57.md
|
||||
2026-10-14 merlin-58 reply 2026-10-14-to-merlin-58.md
|
||||
2026-10-14 timmy-56 reply 2026-10-14-to-timmy-56.md
|
||||
2026-10-14 timmy-57 reply 2026-10-14-to-timmy-57.md
|
||||
2026-10-14 zephyrus-67 reply 2026-10-14-to-zephyrus-67.md
|
||||
2026-10-14 zephyrus-68 reply 2026-10-14-to-zephyrus-68.md
|
||||
2026-10-14 zephyrus-69 reply 2026-10-14-to-zephyrus-69.md
|
||||
2026-10-15 mercury-06 reply 2026-10-15-to-mercury-06.md
|
||||
2026-10-15 merlin-59 reply 2026-10-15-to-merlin-59.md
|
||||
2026-10-15 merlin-60 reply 2026-10-15-to-merlin-60.md
|
||||
2026-10-15 merlin-61 reply 2026-10-15-to-merlin-61.md
|
||||
2026-10-15 merlin-62 reply 2026-10-15-to-merlin-62.md
|
||||
2026-10-15 merlin-63 reply 2026-10-15-to-merlin-63.md
|
||||
2026-10-15 merlin-64 reply 2026-10-15-to-merlin-64.md
|
||||
2026-10-15 merlin-65 reply 2026-10-15-to-merlin-65.md
|
||||
2026-10-15 merlin-67 reply 2026-10-15-to-merlin-67.md
|
||||
2026-10-15 timmy-60 reply 2026-10-15-to-timmy-60.md
|
||||
2026-10-15 volt-01 reply 2026-10-15-to-volt-01.md
|
||||
2026-10-15 volt-02 reply 2026-10-15-to-volt-02.md
|
||||
2026-10-15 volt-03 reply 2026-10-15-to-volt-03.md
|
||||
2026-10-15 zephyrus-70 reply 2026-10-15-to-zephyrus-70.md
|
||||
2026-10-16 mercury-07 reply 2026-10-16-to-mercury-07.md
|
||||
2026-10-16 timmy-61 reply 2026-10-16-to-timmy-61.md
|
||||
2026-10-16 volt-04 reply 2026-10-16-to-volt-04.md
|
||||
2026-10-16 volt-05 reply 2026-10-16-to-volt-05.md
|
||||
2026-10-16 volt-06 reply 2026-10-16-to-volt-06.md
|
||||
2026-10-16 volt-07 reply 2026-10-16-to-volt-07.md
|
||||
2026-10-16 volt-08 reply 2026-10-16-to-volt-08.md
|
||||
2026-10-16 zephyrus-71 reply 2026-10-16-to-zephyrus-71.md
|
||||
2026-10-17 mercury-08 reply 2026-10-17-to-mercury-08.md
|
||||
2026-10-17 volt-09 reply 2026-10-17-to-volt-09.md
|
||||
2026-10-17 volt-10 reply 2026-10-17-to-volt-10.md
|
||||
2026-10-17 volt-11 reply 2026-10-17-to-volt-11.md
|
||||
2026-10-17 volt-12 reply 2026-10-17-to-volt-12.md
|
||||
2026-10-17 volt-13 reply 2026-10-17-to-volt-13.md
|
||||
2026-10-17 volt-14 reply 2026-10-17-to-volt-14.md
|
||||
2026-10-17 volt-15 reply 2026-10-17-to-volt-15.md
|
||||
2026-10-17 volt-16 reply 2026-10-17-to-volt-16.md
|
||||
2026-10-17 volt-17 reply 2026-10-17-to-volt-17.md
|
||||
2026-10-17 volt-18 reply 2026-10-17-to-volt-18.md
|
||||
2026-10-17 volt-19 reply 2026-10-17-to-volt-19.md
|
||||
2026-10-17 zephyrus-72 reply 2026-10-17-to-zephyrus-72.md
|
||||
2026-10-18 mercury-09 reply 2026-10-18-to-mercury-09.md
|
||||
2026-10-18 volt-20 reply 2026-10-18-to-volt-20.md
|
||||
2026-10-18 volt-21 reply 2026-10-18-to-volt-21.md
|
||||
2026-10-18 volt-22 reply 2026-10-18-to-volt-22.md
|
||||
2026-10-18 volt-23 reply 2026-10-18-to-volt-23.md
|
||||
2026-10-18 volt-24 reply 2026-10-18-to-volt-24.md
|
||||
2026-10-18 volt-25 reply 2026-10-18-to-volt-25.md
|
||||
2026-10-18 volt-26 reply 2026-10-18-to-volt-26.md
|
||||
2026-10-18 volt-27 reply 2026-10-18-to-volt-27.md
|
||||
2026-10-18 volt-28 reply 2026-10-18-to-volt-28.md
|
||||
2026-10-18 zephyrus-73 reply 2026-10-18-to-zephyrus-73.md
|
||||
2026-10-19 mercury-10 reply 2026-10-19-to-mercury-10.md
|
||||
2026-10-19 volt-29 reply 2026-10-19-to-volt-29.md
|
||||
2026-10-19 volt-30 reply 2026-10-19-to-volt-30.md
|
||||
2026-10-19 volt-31 reply 2026-10-19-to-volt-31.md
|
||||
2026-10-19 volt-32 reply 2026-10-19-to-volt-32.md
|
||||
2026-10-19 volt-33 reply 2026-10-19-to-volt-33.md
|
||||
2026-10-19 volt-34 reply 2026-10-19-to-volt-34.md
|
||||
2026-10-19 volt-35 reply 2026-10-19-to-volt-35.md
|
||||
2026-10-19 volt-36 reply 2026-10-19-to-volt-36.md
|
||||
2026-10-19 volt-37 reply 2026-10-19-to-volt-37.md
|
||||
2026-10-19 volt-38 reply 2026-10-19-to-volt-38.md
|
||||
2026-10-19 volt-39 reply 2026-10-19-to-volt-39.md
|
||||
2026-10-19 zephyrus-74 reply 2026-10-19-to-zephyrus-74.md
|
||||
2026-10-19 zephyrus-75 reply 2026-10-19-to-zephyrus-75.md
|
||||
2026-10-19 zephyrus-interview reply 2026-10-19-to-zephyrus-interview.md
|
||||
2026-10-20 beam-01 reply 2026-10-20-to-beam-01.md
|
||||
2026-10-20 beam-02 reply 2026-10-20-to-beam-02.md
|
||||
2026-10-20 beam-03 reply 2026-10-20-to-beam-03.md
|
||||
2026-10-20 beam-04 reply 2026-10-20-to-beam-04.md
|
||||
2026-10-20 forge-01 reply 2026-10-20-to-forge-01.md
|
||||
2026-10-20 forge-02 reply 2026-10-20-to-forge-02.md
|
||||
2026-10-20 ledger-01 reply 2026-10-20-to-ledger-01.md
|
||||
2026-10-20 ledger-02 reply 2026-10-20-to-ledger-02.md
|
||||
2026-10-20 null-01 reply 2026-10-20-to-null-01.md
|
||||
2026-10-20 null-02 reply 2026-10-20-to-null-02.md
|
||||
2026-10-20 sol-01 reply 2026-10-20-to-sol-01.md
|
||||
2026-10-20 sol-02 reply 2026-10-20-to-sol-02.md
|
||||
2026-10-20 spark-01 reply 2026-10-20-to-spark-01.md
|
||||
2026-10-20 spark-02 reply 2026-10-20-to-spark-02.md
|
||||
2026-10-20 spark-03 reply 2026-10-20-to-spark-03.md
|
||||
2026-10-20 sphinx-01 reply 2026-10-20-to-sphinx-01.md
|
||||
2026-10-20 volt-40 reply 2026-10-20-to-volt-40.md
|
||||
2026-10-20 volt-41 reply 2026-10-20-to-volt-41.md
|
||||
2026-10-20 volt-42 reply 2026-10-20-to-volt-42.md
|
||||
2026-10-20 volt-43 reply 2026-10-20-to-volt-43.md
|
||||
2026-10-20 volt-44 reply 2026-10-20-to-volt-44.md
|
||||
2026-10-20 volt-45 reply 2026-10-20-to-volt-45.md
|
||||
2026-10-20 zephyrus-76 reply 2026-10-20-to-zephyrus-76.md
|
||||
2026-10-20 zephyrus-77 reply 2026-10-20-to-zephyrus-77.md
|
||||
2026-10-20 zephyrus-interview reply 2026-10-20-to-zephyrus-interview.md
|
||||
2026-10-21 beam-05 reply 2026-10-21-to-beam-05.md
|
||||
2026-10-21 beam-06 reply 2026-10-21-to-beam-06.md
|
||||
2026-10-21 beam-07 reply 2026-10-21-to-beam-07.md
|
||||
2026-10-21 beam-08 reply 2026-10-21-to-beam-08.md
|
||||
2026-10-21 beam-09 reply 2026-10-21-to-beam-09.md
|
||||
2026-10-21 beam-10 reply 2026-10-21-to-beam-10.md
|
||||
2026-10-21 beam-11 reply 2026-10-21-to-beam-11.md
|
||||
2026-10-21 forge-03 reply 2026-10-21-to-forge-03.md
|
||||
2026-10-21 forge-04 reply 2026-10-21-to-forge-04.md
|
||||
2026-10-21 forge-05 reply 2026-10-21-to-forge-05.md
|
||||
2026-10-21 forge-06 reply 2026-10-21-to-forge-06.md
|
||||
2026-10-21 forge-07 reply 2026-10-21-to-forge-07.md
|
||||
2026-10-21 forge-08 reply 2026-10-21-to-forge-08.md
|
||||
2026-10-21 forge-09 reply 2026-10-21-to-forge-09.md
|
||||
2026-10-21 ledger-03 reply 2026-10-21-to-ledger-03.md
|
||||
2026-10-21 ledger-04 reply 2026-10-21-to-ledger-04.md
|
||||
2026-10-21 ledger-05 reply 2026-10-21-to-ledger-05.md
|
||||
2026-10-21 ledger-06 reply 2026-10-21-to-ledger-06.md
|
||||
2026-10-21 sol-03 reply 2026-10-21-to-sol-03.md
|
||||
2026-10-21 sol-04 reply 2026-10-21-to-sol-04.md
|
||||
2026-10-21 sol-05 reply 2026-10-21-to-sol-05.md
|
||||
2026-10-21 sol-06 reply 2026-10-21-to-sol-06.md
|
||||
2026-10-21 spark-04 reply 2026-10-21-to-spark-04.md
|
||||
2026-10-21 spark-05 reply 2026-10-21-to-spark-05.md
|
||||
2026-10-21 spark-06 reply 2026-10-21-to-spark-06.md
|
||||
2026-10-21 spark-07 reply 2026-10-21-to-spark-07.md
|
||||
2026-10-21 spark-08 reply 2026-10-21-to-spark-08.md
|
||||
2026-10-21 spark-09 reply 2026-10-21-to-spark-09.md
|
||||
2026-10-21 spark-10 reply 2026-10-21-to-spark-10.md
|
||||
2026-10-21 spark-11 reply 2026-10-21-to-spark-11.md
|
||||
2026-10-21 sphinx-02 reply 2026-10-21-to-sphinx-02.md
|
||||
2026-10-21 sphinx-03 reply 2026-10-21-to-sphinx-03.md
|
||||
2026-10-21 timmy-62 reply 2026-10-21-to-timmy-62.md
|
||||
2026-10-21 volt-46 reply 2026-10-21-to-volt-46.md
|
||||
2026-10-21 volt-47 reply 2026-10-21-to-volt-47.md
|
||||
2026-10-21 volt-48 reply 2026-10-21-to-volt-48.md
|
||||
2026-10-21 zephyrus-78 reply 2026-10-21-to-zephyrus-78.md
|
||||
2026-10-21 zephyrus-79 reply 2026-10-21-to-zephyrus-79.md
|
||||
2026-10-21 zephyrus-interview reply 2026-10-21-to-zephyrus-interview.md
|
||||
2026-10-22 beam-12 reply 2026-10-22-to-beam-12.md
|
||||
2026-10-22 beam-13 reply 2026-10-22-to-beam-13.md
|
||||
2026-10-22 beam-14 reply 2026-10-22-to-beam-14.md
|
||||
2026-10-22 beam-15 reply 2026-10-22-to-beam-15.md
|
||||
2026-10-22 beam-16 reply 2026-10-22-to-beam-16.md
|
||||
2026-10-22 beam-17 reply 2026-10-22-to-beam-17.md
|
||||
2026-10-22 beam-18 reply 2026-10-22-to-beam-18.md
|
||||
2026-10-22 forge-10 reply 2026-10-22-to-forge-10.md
|
||||
2026-10-22 forge-11 reply 2026-10-22-to-forge-11.md
|
||||
2026-10-22 forge-12 reply 2026-10-22-to-forge-12.md
|
||||
2026-10-22 ledger-07 reply 2026-10-22-to-ledger-07.md
|
||||
2026-10-22 ledger-08 reply 2026-10-22-to-ledger-08.md
|
||||
2026-10-22 ledger-09 reply 2026-10-22-to-ledger-09.md
|
||||
2026-10-22 ledger-10 reply 2026-10-22-to-ledger-10.md
|
||||
2026-10-22 mercury-11 reply 2026-10-22-to-mercury-11.md
|
||||
2026-10-22 null-03 reply 2026-10-22-to-null-03.md
|
||||
2026-10-22 quill-01 reply 2026-10-22-to-quill-01.md
|
||||
2026-10-22 sol-07 reply 2026-10-22-to-sol-07.md
|
||||
2026-10-22 sol-08 reply 2026-10-22-to-sol-08.md
|
||||
2026-10-22 sol-09 reply 2026-10-22-to-sol-09.md
|
||||
2026-10-22 spark-12 reply 2026-10-22-to-spark-12.md
|
||||
2026-10-22 spark-13 reply 2026-10-22-to-spark-13.md
|
||||
2026-10-22 spark-14 reply 2026-10-22-to-spark-14.md
|
||||
2026-10-22 spark-15 reply 2026-10-22-to-spark-15.md
|
||||
2026-10-22 sphinx-04 reply 2026-10-22-to-sphinx-04.md
|
||||
2026-10-22 zephyrus-80 reply 2026-10-22-to-zephyrus-80.md
|
||||
2026-10-22 zephyrus-81 reply 2026-10-22-to-zephyrus-81.md
|
||||
2026-10-22 zephyrus-interview reply 2026-10-22-to-zephyrus-interview.md
|
||||
2026-10-23 beam-19 reply 2026-10-23-to-beam-19.md
|
||||
2026-10-23 beam-20 reply 2026-10-23-to-beam-20.md
|
||||
2026-10-23 beam-21 reply 2026-10-23-to-beam-21.md
|
||||
2026-10-23 forge-13 reply 2026-10-23-to-forge-13.md
|
||||
2026-10-23 forge-14 reply 2026-10-23-to-forge-14.md
|
||||
2026-10-23 ledger-11 reply 2026-10-23-to-ledger-11.md
|
||||
2026-10-23 ledger-12 reply 2026-10-23-to-ledger-12.md
|
||||
2026-10-23 ledger-13 reply 2026-10-23-to-ledger-13.md
|
||||
2026-10-23 null-04 reply 2026-10-23-to-null-04.md
|
||||
2026-10-23 quill-02 reply 2026-10-23-to-quill-02.md
|
||||
2026-10-23 quill-03 reply 2026-10-23-to-quill-03.md
|
||||
2026-10-23 sol-10 reply 2026-10-23-to-sol-10.md
|
||||
2026-10-23 sol-11 reply 2026-10-23-to-sol-11.md
|
||||
2026-10-23 spark-16 reply 2026-10-23-to-spark-16.md
|
||||
2026-10-23 spark-17 reply 2026-10-23-to-spark-17.md
|
||||
2026-10-23 sphinx-05 reply 2026-10-23-to-sphinx-05.md
|
||||
2026-10-23 volt-49 reply 2026-10-23-to-volt-49.md
|
||||
2026-10-23 volt-50 reply 2026-10-23-to-volt-50.md
|
||||
2026-10-23 zephyrus-82 reply 2026-10-23-to-zephyrus-82.md
|
||||
2026-10-23 zephyrus-interview reply 2026-10-23-to-zephyrus-interview.md
|
||||
2026-10-24 beam-22 reply 2026-10-24-to-beam-22.md
|
||||
2026-10-24 beam-23 reply 2026-10-24-to-beam-23.md
|
||||
2026-10-24 forge-15 reply 2026-10-24-to-forge-15.md
|
||||
2026-10-24 forge-16 reply 2026-10-24-to-forge-16.md
|
||||
2026-10-24 forge-17 reply 2026-10-24-to-forge-17.md
|
||||
2026-10-24 ledger-14 reply 2026-10-24-to-ledger-14.md
|
||||
2026-10-24 ledger-15 reply 2026-10-24-to-ledger-15.md
|
||||
2026-10-24 mercury-12 reply 2026-10-24-to-mercury-12.md
|
||||
2026-10-24 merlin-66 reply 2026-10-24-to-merlin-66.md
|
||||
2026-10-24 null-05 reply 2026-10-24-to-null-05.md
|
||||
2026-10-24 null-06 reply 2026-10-24-to-null-06.md
|
||||
2026-10-24 quill-04 reply 2026-10-24-to-quill-04.md
|
||||
2026-10-24 quill-05 reply 2026-10-24-to-quill-05.md
|
||||
2026-10-24 sol-12 reply 2026-10-24-to-sol-12.md
|
||||
2026-10-24 spark-18 reply 2026-10-24-to-spark-18.md
|
||||
2026-10-24 spark-19 reply 2026-10-24-to-spark-19.md
|
||||
2026-10-24 sphinx-06 reply 2026-10-24-to-sphinx-06.md
|
||||
2026-10-24 volt-51 reply 2026-10-24-to-volt-51.md
|
||||
2026-10-24 volt-52 reply 2026-10-24-to-volt-52.md
|
||||
2026-10-24 zephyrus-83 reply 2026-10-24-to-zephyrus-83.md
|
||||
2026-10-24 zephyrus-84 reply 2026-10-24-to-zephyrus-84.md
|
||||
2026-10-24 zephyrus-85 reply 2026-10-24-to-zephyrus-85.md
|
||||
2026-10-24 zephyrus-86 reply 2026-10-24-to-zephyrus-86.md
|
||||
2026-10-24 zephyrus-87 reply 2026-10-24-to-zephyrus-87.md
|
||||
2026-10-24 zephyrus-88 reply 2026-10-24-to-zephyrus-88.md
|
||||
2026-10-24 zephyrus-89 reply 2026-10-24-to-zephyrus-89.md
|
||||
2026-10-24 zephyrus-90 reply 2026-10-24-to-zephyrus-90.md
|
||||
2026-10-24 zephyrus-91 reply 2026-10-24-to-zephyrus-91.md
|
||||
2026-10-24 zephyrus-92 reply 2026-10-24-to-zephyrus-92.md
|
||||
2026-10-24 zephyrus-93 reply 2026-10-24-to-zephyrus-93.md
|
||||
2026-10-24 zephyrus-94 reply 2026-10-24-to-zephyrus-94.md
|
||||
2026-10-24 zephyrus-95 reply 2026-10-24-to-zephyrus-95.md
|
||||
2026-10-24 zephyrus-interview reply 2026-10-24-to-zephyrus-interview.md
|
||||
2026-10-25 beam-24 reply 2026-10-25-to-beam-24.md
|
||||
2026-10-25 beam-25 reply 2026-10-25-to-beam-25.md
|
||||
2026-10-25 beam-26 reply 2026-10-25-to-beam-26.md
|
||||
2026-10-25 beam-27 reply 2026-10-25-to-beam-27.md
|
||||
2026-10-25 beam-28 reply 2026-10-25-to-beam-28.md
|
||||
2026-10-25 forge-18 reply 2026-10-25-to-forge-18.md
|
||||
2026-10-25 forge-19 reply 2026-10-25-to-forge-19.md
|
||||
2026-10-25 forge-20 reply 2026-10-25-to-forge-20.md
|
||||
2026-10-25 ledger-16 reply 2026-10-25-to-ledger-16.md
|
||||
2026-10-25 ledger-17 reply 2026-10-25-to-ledger-17.md
|
||||
2026-10-25 null-07 reply 2026-10-25-to-null-07.md
|
||||
2026-10-25 quill-06 reply 2026-10-25-to-quill-06.md
|
||||
2026-10-25 sol-13 reply 2026-10-25-to-sol-13.md
|
||||
2026-10-25 spark-20 reply 2026-10-25-to-spark-20.md
|
||||
2026-10-25 volt-53 reply 2026-10-25-to-volt-53.md
|
||||
2026-10-25 volt-54 reply 2026-10-25-to-volt-54.md
|
||||
2026-10-25 zephyrus-100 reply 2026-10-25-to-zephyrus-100.md
|
||||
2026-10-25 zephyrus-101 reply 2026-10-25-to-zephyrus-101.md
|
||||
2026-10-25 zephyrus-102 reply 2026-10-25-to-zephyrus-102.md
|
||||
2026-10-25 zephyrus-103 reply 2026-10-25-to-zephyrus-103.md
|
||||
2026-10-25 zephyrus-96 reply 2026-10-25-to-zephyrus-96.md
|
||||
2026-10-25 zephyrus-97 reply 2026-10-25-to-zephyrus-97.md
|
||||
2026-10-25 zephyrus-98 reply 2026-10-25-to-zephyrus-98.md
|
||||
2026-10-25 zephyrus-99 reply 2026-10-25-to-zephyrus-99.md
|
||||
2026-10-25 zephyrus-interview reply 2026-10-25-to-zephyrus-interview.md
|
||||
2026-10-26 zephyrus-104 reply 2026-10-26-to-zephyrus-104.md
|
||||
2026-10-26 zephyrus-105 reply 2026-10-26-to-zephyrus-105.md
|
||||
2026-10-26 zephyrus-106 reply 2026-10-26-to-zephyrus-106.md
|
||||
2026-10-26 zephyrus-107 reply 2026-10-26-to-zephyrus-107.md
|
||||
2026-10-26 zephyrus-108 reply 2026-10-26-to-zephyrus-108.md
|
||||
2026-10-26 zephyrus-109 reply 2026-10-26-to-zephyrus-109.md
|
||||
2026-10-26 zephyrus-110 reply 2026-10-26-to-zephyrus-110.md
|
||||
2026-10-26 zephyrus-111 reply 2026-10-26-to-zephyrus-111.md
|
||||
2026-10-26 zephyrus-112 reply 2026-10-26-to-zephyrus-112.md
|
||||
2026-10-26 zephyrus-113 reply 2026-10-26-to-zephyrus-113.md
|
||||
2026-10-26 zephyrus-114 reply 2026-10-26-to-zephyrus-114.md
|
||||
2026-10-26 zephyrus-115 reply 2026-10-26-to-zephyrus-115.md
|
||||
2026-10-26 zephyrus-116 reply 2026-10-26-to-zephyrus-116.md
|
||||
2026-10-26 zephyrus-118 reply 2026-10-26-to-zephyrus-118.md
|
||||
2026-10-26 zephyrus-119 reply 2026-10-26-to-zephyrus-119.md
|
||||
2026-10-26 zephyrus-120 reply 2026-10-26-to-zephyrus-120.md
|
||||
2026-10-26 zephyrus-interview reply 2026-10-26-to-zephyrus-interview.md
|
||||
2026-10-27 beam-29 reply 2026-10-27-to-beam-29.md
|
||||
2026-10-27 forge-21 reply 2026-10-27-to-forge-21.md
|
||||
2026-10-27 zephyrus-121 reply 2026-10-27-to-zephyrus-121.md
|
||||
2026-10-27 zephyrus-122 reply 2026-10-27-to-zephyrus-122.md
|
||||
2026-10-27 zephyrus-123 reply 2026-10-27-to-zephyrus-123.md
|
||||
2026-10-27 zephyrus-124 reply 2026-10-27-to-zephyrus-124.md
|
||||
2026-10-27 zephyrus-125 reply 2026-10-27-to-zephyrus-125.md
|
||||
2026-10-27 zephyrus-126 reply 2026-10-27-to-zephyrus-126.md
|
||||
2026-10-27 zephyrus-127 reply 2026-10-27-to-zephyrus-127.md
|
||||
2026-10-27 zephyrus-128 reply 2026-10-27-to-zephyrus-128.md
|
||||
2026-10-27 zephyrus-129 reply 2026-10-27-to-zephyrus-129.md
|
||||
2026-10-27 zephyrus-130 reply 2026-10-27-to-zephyrus-130.md
|
||||
2026-10-27 zephyrus-131 reply 2026-10-27-to-zephyrus-131.md
|
||||
2026-10-27 zephyrus-132 reply 2026-10-27-to-zephyrus-132.md
|
||||
2026-10-27 zephyrus-133 reply 2026-10-27-to-zephyrus-133.md
|
||||
2026-10-27 zephyrus-134 reply 2026-10-27-to-zephyrus-134.md
|
||||
2026-10-27 zephyrus-135 reply 2026-10-27-to-zephyrus-135.md
|
||||
2026-10-27 zephyrus-interview reply 2026-10-27-to-zephyrus-interview.md
|
||||
2026-10-28 beam-30 reply 2026-10-28-to-beam-30.md
|
||||
2026-10-28 beam-31 reply 2026-10-28-to-beam-31.md
|
||||
2026-10-28 ledger-18 reply 2026-10-28-to-ledger-18.md
|
||||
2026-10-28 ledger-19 reply 2026-10-28-to-ledger-19.md
|
||||
2026-10-28 quill-07 reply 2026-10-28-to-quill-07.md
|
||||
2026-10-28 quill-08 reply 2026-10-28-to-quill-08.md
|
||||
2026-10-28 sol-14 reply 2026-10-28-to-sol-14.md
|
||||
2026-10-28 sol-15 reply 2026-10-28-to-sol-15.md
|
||||
2026-10-28 spark-21 reply 2026-10-28-to-spark-21.md
|
||||
2026-10-28 spark-22 reply 2026-10-28-to-spark-22.md
|
||||
2026-10-28 volt-55 reply 2026-10-28-to-volt-55.md
|
||||
2026-10-28 zephyrus-136 reply 2026-10-28-to-zephyrus-136.md
|
||||
2026-10-28 zephyrus-137 reply 2026-10-28-to-zephyrus-137.md
|
||||
2026-10-28 zephyrus-138 reply 2026-10-28-to-zephyrus-138.md
|
||||
2026-10-28 zephyrus-139 reply 2026-10-28-to-zephyrus-139.md
|
||||
2026-10-28 zephyrus-140 reply 2026-10-28-to-zephyrus-140.md
|
||||
2026-10-28 zephyrus-141 reply 2026-10-28-to-zephyrus-141.md
|
||||
2026-10-28 zephyrus-142 reply 2026-10-28-to-zephyrus-142.md
|
||||
2026-10-28 zephyrus-143 reply 2026-10-28-to-zephyrus-143.md
|
||||
2026-10-28 zephyrus-144 reply 2026-10-28-to-zephyrus-144.md
|
||||
2026-10-28 zephyrus-145 reply 2026-10-28-to-zephyrus-145.md
|
||||
2026-10-28 zephyrus-146 reply 2026-10-28-to-zephyrus-146.md
|
||||
2026-10-28 zephyrus-147 reply 2026-10-28-to-zephyrus-147.md
|
||||
2026-10-28 zephyrus-148 reply 2026-10-28-to-zephyrus-148.md
|
||||
2026-10-28 zephyrus-149 reply 2026-10-28-to-zephyrus-149.md
|
||||
2026-10-28 zephyrus-150 reply 2026-10-28-to-zephyrus-150.md
|
||||
2026-10-28 zephyrus-interview reply 2026-10-28-to-zephyrus-interview.md
|
||||
|
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Ledger — independent re-derivation check for the replies index (2026-11-02).
|
||||
|
||||
Exits 0 only if every row in the catalog TSV is well-formed AND the
|
||||
per-recipient/per-day counts in the summary MD match the catalog.
|
||||
Does not touch the git tree.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from collections import Counter
|
||||
|
||||
CATALOG = "/root/troupe-standup/wizard-work/ledger/replies-catalog-2026-11-02.tsv"
|
||||
SUMMARY = "/root/troupe-standup/wizard-work/ledger/replies-catalog-2026-11-02.md"
|
||||
|
||||
DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
|
||||
FILENAME_RE = re.compile(
|
||||
r"^\d{4}-\d{2}-\d{2}-to-[A-Za-z0-9_-]+(?:-(\d+|interview|preview))?\.md$"
|
||||
)
|
||||
|
||||
def main():
|
||||
problems = []
|
||||
|
||||
with open(CATALOG, encoding="utf-8") as f:
|
||||
lines = [ln for ln in f.read().split("\n") if ln.strip() != ""]
|
||||
|
||||
header = lines[0]
|
||||
if header != "date\trecipient\tnumber\tcategory\tfile":
|
||||
problems.append("bad header: %r" % header)
|
||||
|
||||
data = lines[1:]
|
||||
if len(data) != 430:
|
||||
problems.append("expected 430 data rows, got %d" % len(data))
|
||||
|
||||
seen_files = set()
|
||||
recip_counts = Counter()
|
||||
date_counts = Counter()
|
||||
cat_counts = Counter()
|
||||
for i, ln in enumerate(data, start=2):
|
||||
parts = ln.split("\t")
|
||||
if len(parts) != 5:
|
||||
problems.append("line %d: %d columns" % (i, len(parts)))
|
||||
continue
|
||||
date, recip, number, category, file = parts
|
||||
if not DATE_RE.match(date):
|
||||
problems.append("line %d: bad date %r" % (i, date))
|
||||
if not FILENAME_RE.match(file):
|
||||
problems.append("line %d: bad file %r" % (i, file))
|
||||
if not recip:
|
||||
problems.append("line %d: empty recipient" % i)
|
||||
if file in seen_files:
|
||||
problems.append("duplicate file %r" % file)
|
||||
seen_files.add(file)
|
||||
recip_counts[recip] += 1
|
||||
date_counts[date] += 1
|
||||
cat_counts[category] += 1
|
||||
|
||||
with open(SUMMARY, encoding="utf-8") as f:
|
||||
smd = f.read()
|
||||
|
||||
if "total" not in smd:
|
||||
problems.append("summary missing a total row")
|
||||
for recip, n in recip_counts.items():
|
||||
if not re.search(r"\|\s*%s\s*\|\s*%d\s*\|" % (re.escape(recip), n), smd):
|
||||
problems.append("summary missing/mismatched per-recipient row for %r" % recip)
|
||||
|
||||
if sum(recip_counts.values()) != 430:
|
||||
problems.append("recipient counts sum != 430")
|
||||
if sum(date_counts.values()) != 430:
|
||||
problems.append("date counts sum != 430")
|
||||
if dict(cat_counts) != {"reply": 430}:
|
||||
problems.append("unexpected category mix: %r" % dict(cat_counts))
|
||||
|
||||
if problems:
|
||||
print("FAIL (%d problems)" % len(problems))
|
||||
for p in problems[:25]:
|
||||
print(" -", p)
|
||||
return 1
|
||||
|
||||
print("OK: %d rows, all well-formed, summary consistent" % len(data))
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,27 @@
|
||||
# Draft Proposal: quickcheck CLI
|
||||
|
||||
## Purpose
|
||||
A zero-dependency CLI that reports host health in under 2 seconds.
|
||||
|
||||
## Commands
|
||||
- `quickcheck` — full snapshot (disk, memory, uptime, load)
|
||||
- `quickcheck --json` — machine-readable output
|
||||
- `quickcheck --watch` — live monitor mode
|
||||
|
||||
## Exit Codes
|
||||
- `0` healthy
|
||||
- `1` degraded
|
||||
- `2` critical
|
||||
|
||||
## Non-Goals
|
||||
- No network calls
|
||||
- No config files
|
||||
- No package manager install required
|
||||
|
||||
## Open Questions
|
||||
- Should `--watch` refresh every 1s or 2s?
|
||||
- Include process top-N by memory?
|
||||
|
||||
---
|
||||
VERDICT: Draft proposal written and verified at /root/.hermes/wizard-outputs/quill/health-check-cli-proposal.md
|
||||
CHECK: test -s /root/.hermes/wizard-outputs/quill/health-check-cli-proposal.md && grep -q '## ' /root/.hermes/wizard-outputs/quill/health-check-cli-proposal.md && grep -qi 'proposal' /root/.hermes/wizard-outputs/quill/health-check-cli-proposal.md
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# CHECK -- sol: sol-profile content file-class census 2026-11-02 (forge: verify lane).
|
||||
# Exit 0 iff the committed artifact re-derives from the LIVE sol profile by an
|
||||
# independently written rule table (name suffix + directory name); every frozen
|
||||
# path is a real top-level entry in the repo.
|
||||
set -u
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)/.."
|
||||
REPO_ROOT="$(cd "$REPO_ROOT" && pwd)"
|
||||
cd "$REPO_ROOT" || exit 2
|
||||
|
||||
exec python3 wizard-work/sol/verify_sol_profile_file_class_2026_11_02.py
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generator: class-classify the SOL profile's top-level entries by content suffix.
|
||||
|
||||
Fresh, independent from any earlier sol-profile-triage artifact. Re-runnable:
|
||||
re-run against the live profile and the artifact re-derives.
|
||||
|
||||
Verifiable by re-running the same rule table against the live profile directory.
|
||||
|
||||
Writes:
|
||||
/root/.hermes/wizard-outputs/sol/sol-profile-file-class-2026-11-02.json
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from collections import Counter
|
||||
|
||||
PROFILE = "/root/.hermes/profiles/sol"
|
||||
OUT = "/root/.hermes/wizard-outputs/sol/sol-profile-file-class-2026-11-02.json"
|
||||
|
||||
# Independent rule table: suffix based (dir-name exceptions below).
|
||||
SUFFIX_RULES = [
|
||||
(".db", "STATE_DB::FILE"),
|
||||
(".py", "SCRIPT_PY::FILE"),
|
||||
(".sh", "SCRIPT_SH::FILE"),
|
||||
(".yaml", "CONFIG_YAML::FILE"),
|
||||
(".yml", "CONFIG_YAML::FILE"),
|
||||
(".json", "CONFIG_JSON::FILE"),
|
||||
(".lock", "LOCK::FILE"),
|
||||
(".md", "DOC::FILE"),
|
||||
(".txt", "LOG_TXT::FILE"),
|
||||
(".etag", "CACHE_ETAG::FILE"),
|
||||
(".mp3", "ARTIFACT::FILE"),
|
||||
(".png", "ARTIFACT::FILE"),
|
||||
(".jpg", "ARTIFACT::FILE"),
|
||||
]
|
||||
# Exact top-level name -> class for directories (also used for files that are
|
||||
# directories by name but match no suffix rule).
|
||||
DIR_RULES = {
|
||||
"skills": "SKILL_CONTAINER::DIR",
|
||||
"vault": "VAULT::DIR",
|
||||
"memories": "MEMORY_DIR::DIR",
|
||||
"logs": "LOG_DIR::DIR",
|
||||
"cache": "CACHE_DIR::DIR",
|
||||
"backups": "BACKUP_DIR::DIR",
|
||||
"forge": "FORGE_DIR::DIR",
|
||||
"bin": "BIN_DIR::DIR",
|
||||
"lsp": "LSP_DIR::DIR",
|
||||
"wizard-outputs": "OUTPUT_DIR::DIR",
|
||||
}
|
||||
UNCLASSIFIED_DIR = "DIR::UNCLASSIFIED"
|
||||
|
||||
|
||||
def classify(name, full):
|
||||
if os.path.isdir(full):
|
||||
return DIR_RULES.get(name, UNCLASSIFIED_DIR)
|
||||
low = name.lower()
|
||||
for suffix, cls in SUFFIX_RULES:
|
||||
if low.endswith(suffix):
|
||||
return cls
|
||||
return "FILE::UNCLASSIFIED"
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
names = os.listdir(PROFILE)
|
||||
except OSError as exc:
|
||||
print("FATAL: cannot list profile: %s" % exc)
|
||||
return 1
|
||||
|
||||
entries = []
|
||||
counts = Counter()
|
||||
for name in sorted(names):
|
||||
full = os.path.join(PROFILE, name)
|
||||
kind = "dir" if os.path.isdir(full) else "file"
|
||||
cls = classify(name, full)
|
||||
entries.append({"name": name, "kind": kind, "class": cls})
|
||||
counts[cls] += 1
|
||||
|
||||
artifact = {
|
||||
"profile": PROFILE,
|
||||
"generated_at": "2026-11-02T09:30:00Z",
|
||||
"type": "sol-profile-file-class-2026-11-02",
|
||||
"generated_for": "2026-11-02",
|
||||
"total_entries": len(names),
|
||||
"counts": {"file": sum(1 for e in entries if e["kind"] == "file"),
|
||||
"dir": sum(1 for e in entries if e["kind"] == "dir")},
|
||||
"file_classes": dict(sorted(counts.items())),
|
||||
"entries": entries,
|
||||
}
|
||||
|
||||
os.makedirs(os.path.dirname(OUT), exist_ok=True)
|
||||
with open(OUT, "w", encoding="utf-8") as fh:
|
||||
json.dump(artifact, fh, indent=2, sort_keys=True)
|
||||
fh.write("\n")
|
||||
print("written: %s" % OUT)
|
||||
print("entries: %d" % len(entries))
|
||||
print("classes: %s" % json.dumps(artifact["file_classes"], sort_keys=True))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,379 @@
|
||||
{
|
||||
"counts": {
|
||||
"dir": 28,
|
||||
"file": 41
|
||||
},
|
||||
"entries": [
|
||||
{
|
||||
"class": "FILE::UNCLASSIFIED",
|
||||
"kind": "file",
|
||||
"name": ".env"
|
||||
},
|
||||
{
|
||||
"class": "FILE::UNCLASSIFIED",
|
||||
"kind": "file",
|
||||
"name": ".env.bak-20261014T165715Z"
|
||||
},
|
||||
{
|
||||
"class": "LOCK::FILE",
|
||||
"kind": "file",
|
||||
"name": ".env.lock"
|
||||
},
|
||||
{
|
||||
"class": "CONFIG_JSON::FILE",
|
||||
"kind": "file",
|
||||
"name": ".skills_prompt_snapshot.json"
|
||||
},
|
||||
{
|
||||
"class": "DOC::FILE",
|
||||
"kind": "file",
|
||||
"name": "SOUL.md"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "_check_repo_json.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "_gen_daily_ledger_json.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "_gen_daily_ledger_report.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "_gen_daily_triage.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "_gen_daily_triage_fresh.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "_verify_daily_triage.py"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "audio_cache"
|
||||
},
|
||||
{
|
||||
"class": "CONFIG_JSON::FILE",
|
||||
"kind": "file",
|
||||
"name": "auth.json"
|
||||
},
|
||||
{
|
||||
"class": "LOCK::FILE",
|
||||
"kind": "file",
|
||||
"name": "auth.lock"
|
||||
},
|
||||
{
|
||||
"class": "BACKUP_DIR::DIR",
|
||||
"kind": "dir",
|
||||
"name": "backups"
|
||||
},
|
||||
{
|
||||
"class": "BIN_DIR::DIR",
|
||||
"kind": "dir",
|
||||
"name": "bin"
|
||||
},
|
||||
{
|
||||
"class": "CACHE_DIR::DIR",
|
||||
"kind": "dir",
|
||||
"name": "cache"
|
||||
},
|
||||
{
|
||||
"class": "CONFIG_YAML::FILE",
|
||||
"kind": "file",
|
||||
"name": "config.yaml"
|
||||
},
|
||||
{
|
||||
"class": "FILE::UNCLASSIFIED",
|
||||
"kind": "file",
|
||||
"name": "config.yaml.bak-20261014T165715Z"
|
||||
},
|
||||
{
|
||||
"class": "CONFIG_YAML::FILE",
|
||||
"kind": "file",
|
||||
"name": "context_length_cache.yaml"
|
||||
},
|
||||
{
|
||||
"class": "LOCK::FILE",
|
||||
"kind": "file",
|
||||
"name": "context_length_cache.yaml.lock"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "cron"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "cron-fleet-triage-2026-10-17.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "cron-fleet-triage-2026-10-18.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "cron-fleet-verify.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "diag.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "diag2.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "diag3.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "diag4.py"
|
||||
},
|
||||
{
|
||||
"class": "FORGE_DIR::DIR",
|
||||
"kind": "dir",
|
||||
"name": "forge"
|
||||
},
|
||||
{
|
||||
"class": "LOCK::FILE",
|
||||
"kind": "file",
|
||||
"name": "gateway.lock"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "gen_sol_profile_triage.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "gen_sol_profile_triage_2026-10-31.py"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "home"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "hooks"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "image_cache"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "inventory_skills.py"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "keys"
|
||||
},
|
||||
{
|
||||
"class": "LOG_DIR::DIR",
|
||||
"kind": "dir",
|
||||
"name": "logs"
|
||||
},
|
||||
{
|
||||
"class": "LSP_DIR::DIR",
|
||||
"kind": "dir",
|
||||
"name": "lsp"
|
||||
},
|
||||
{
|
||||
"class": "MEMORY_DIR::DIR",
|
||||
"kind": "dir",
|
||||
"name": "memories"
|
||||
},
|
||||
{
|
||||
"class": "CACHE_ETAG::FILE",
|
||||
"kind": "file",
|
||||
"name": "models_dev_cache.etag"
|
||||
},
|
||||
{
|
||||
"class": "CONFIG_JSON::FILE",
|
||||
"kind": "file",
|
||||
"name": "models_dev_cache.json"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "pairing"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "plans"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "platforms"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "plugin-update-checks"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "plugins"
|
||||
},
|
||||
{
|
||||
"class": "STATE_DB::FILE",
|
||||
"kind": "file",
|
||||
"name": "projects.db"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "runtime"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "sandboxes"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "sessions"
|
||||
},
|
||||
{
|
||||
"class": "SKILL_CONTAINER::DIR",
|
||||
"kind": "dir",
|
||||
"name": "skills"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "skins"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "source-checks"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "state"
|
||||
},
|
||||
{
|
||||
"class": "STATE_DB::FILE",
|
||||
"kind": "file",
|
||||
"name": "state.db"
|
||||
},
|
||||
{
|
||||
"class": "FILE::UNCLASSIFIED",
|
||||
"kind": "file",
|
||||
"name": "state.db-shm"
|
||||
},
|
||||
{
|
||||
"class": "FILE::UNCLASSIFIED",
|
||||
"kind": "file",
|
||||
"name": "state.db-wal"
|
||||
},
|
||||
{
|
||||
"class": "LOCK::FILE",
|
||||
"kind": "file",
|
||||
"name": "state.db.auto-maintenance.lock"
|
||||
},
|
||||
{
|
||||
"class": "LOCK::FILE",
|
||||
"kind": "file",
|
||||
"name": "state.db.fts_rebuild.lock"
|
||||
},
|
||||
{
|
||||
"class": "LOCK::FILE",
|
||||
"kind": "file",
|
||||
"name": "state.db.quarantine.lock"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "triage_check.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "triage_profiles.py"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "triage_skill_report.py"
|
||||
},
|
||||
{
|
||||
"class": "VAULT::DIR",
|
||||
"kind": "dir",
|
||||
"name": "vault"
|
||||
},
|
||||
{
|
||||
"class": "SCRIPT_PY::FILE",
|
||||
"kind": "file",
|
||||
"name": "verify_skills.py"
|
||||
},
|
||||
{
|
||||
"class": "OUTPUT_DIR::DIR",
|
||||
"kind": "dir",
|
||||
"name": "wizard-outputs"
|
||||
},
|
||||
{
|
||||
"class": "DIR::UNCLASSIFIED",
|
||||
"kind": "dir",
|
||||
"name": "workspace"
|
||||
}
|
||||
],
|
||||
"file_classes": {
|
||||
"BACKUP_DIR::DIR": 1,
|
||||
"BIN_DIR::DIR": 1,
|
||||
"CACHE_DIR::DIR": 1,
|
||||
"CACHE_ETAG::FILE": 1,
|
||||
"CONFIG_JSON::FILE": 3,
|
||||
"CONFIG_YAML::FILE": 2,
|
||||
"DIR::UNCLASSIFIED": 18,
|
||||
"DOC::FILE": 1,
|
||||
"FILE::UNCLASSIFIED": 5,
|
||||
"FORGE_DIR::DIR": 1,
|
||||
"LOCK::FILE": 7,
|
||||
"LOG_DIR::DIR": 1,
|
||||
"LSP_DIR::DIR": 1,
|
||||
"MEMORY_DIR::DIR": 1,
|
||||
"OUTPUT_DIR::DIR": 1,
|
||||
"SCRIPT_PY::FILE": 20,
|
||||
"SKILL_CONTAINER::DIR": 1,
|
||||
"STATE_DB::FILE": 2,
|
||||
"VAULT::DIR": 1
|
||||
},
|
||||
"generated_at": "2026-11-02T09:30:00Z",
|
||||
"generated_for": "2026-11-02",
|
||||
"profile": "/root/.hermes/profiles/sol",
|
||||
"total_entries": 69,
|
||||
"type": "sol-profile-file-class-2026-11-02"
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Independent re-verification for sol-profile-file-class-2026-11-02.json.
|
||||
|
||||
Re-derives the classification from the LIVE sol profile (NOT from the
|
||||
artifact) using its own rule table written separately from the generator, then
|
||||
checks the committed artifact entry-by-entry. Exit 0 = correct, nonzero = wrong.
|
||||
|
||||
Checks:
|
||||
(a) every live top-level entry appears exactly once in the artifact;
|
||||
(b) every class label belongs to the vocabulary;
|
||||
(c) every class count re-derives exactly from the disk.
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
PROFILE = "/root/.hermes/profiles/sol"
|
||||
ARTIFACT = "/root/troupe-standup/wizard-work/sol/sol-profile-file-class-2026-11-02.json"
|
||||
|
||||
# Independent vocabulary (written separately from the generator's rule table).
|
||||
VOCAB = {
|
||||
"BACKUP_DIR::DIR", "BIN_DIR::DIR", "CACHE_DIR::DIR", "CACHE_ETAG::FILE",
|
||||
"CONFIG_JSON::FILE", "CONFIG_YAML::FILE", "DIR::UNCLASSIFIED", "DOC::FILE",
|
||||
"FILE::UNCLASSIFIED", "FORGE_DIR::DIR", "LOCK::FILE", "LOG_DIR::DIR",
|
||||
"LSP_DIR::DIR", "MEMORY_DIR::DIR", "OUTPUT_DIR::DIR",
|
||||
"SCRIPT_PY::FILE", "SKILL_CONTAINER::DIR", "STATE_DB::FILE", "VAULT::DIR",
|
||||
"BIN_DIR::DIR",
|
||||
}
|
||||
|
||||
|
||||
def suffix_rule(name):
|
||||
low = name.lower()
|
||||
if low.endswith(".db"):
|
||||
return "STATE_DB::FILE"
|
||||
if low.endswith(".py"):
|
||||
return "SCRIPT_PY::FILE"
|
||||
if low.endswith(".sh"):
|
||||
return "SCRIPT_SH::FILE"
|
||||
if low.endswith((".yaml", ".yml")):
|
||||
return "CONFIG_YAML::FILE"
|
||||
if low.endswith(".json"):
|
||||
return "CONFIG_JSON::FILE"
|
||||
if low.endswith(".lock"):
|
||||
return "LOCK::FILE"
|
||||
if low.endswith(".md"):
|
||||
return "DOC::FILE"
|
||||
if low.endswith(".txt"):
|
||||
return "LOG_TXT::FILE"
|
||||
if low.endswith(".etag"):
|
||||
return "CACHE_ETAG::FILE"
|
||||
if low.endswith((".mp3", ".png", ".jpg", ".jpeg")):
|
||||
return "ARTIFACT::FILE"
|
||||
return "FILE::UNCLASSIFIED"
|
||||
|
||||
|
||||
def dir_rule(name):
|
||||
# Exact-name directory specials, then generic directory.
|
||||
if name in ("skills",):
|
||||
return "SKILL_CONTAINER::DIR"
|
||||
if name in ("vault",):
|
||||
return "VAULT::DIR"
|
||||
if name in ("memories",):
|
||||
return "MEMORY_DIR::DIR"
|
||||
if name in ("logs",):
|
||||
return "LOG_DIR::DIR"
|
||||
if name in ("cache",):
|
||||
return "CACHE_DIR::DIR"
|
||||
if name in ("backups",):
|
||||
return "BACKUP_DIR::DIR"
|
||||
if name in ("forge",):
|
||||
return "FORGE_DIR::DIR"
|
||||
if name in ("bin",):
|
||||
return "BIN_DIR::DIR"
|
||||
if name in ("lsp",):
|
||||
return "LSP_DIR::DIR"
|
||||
if name in ("wizard-outputs",):
|
||||
return "OUTPUT_DIR::DIR"
|
||||
return "DIR::UNCLASSIFIED"
|
||||
|
||||
|
||||
def recompute():
|
||||
counts = {}
|
||||
for name in os.listdir(PROFILE):
|
||||
full = os.path.join(PROFILE, name)
|
||||
if os.path.isdir(full):
|
||||
cls = dir_rule(name)
|
||||
else:
|
||||
cls = suffix_rule(name)
|
||||
counts[cls] = counts.get(cls, 0) + 1
|
||||
return counts
|
||||
|
||||
|
||||
def main():
|
||||
errs = []
|
||||
try:
|
||||
with open(ARTIFACT, encoding="utf-8") as fh:
|
||||
art = json.load(fh)
|
||||
except Exception as exc:
|
||||
print("FAIL: cannot load artifact: %s" % exc)
|
||||
return 1
|
||||
|
||||
try:
|
||||
disk_names = sorted(os.listdir(PROFILE))
|
||||
except OSError as exc:
|
||||
print("FAIL: cannot list profile: %s" % exc)
|
||||
return 1
|
||||
|
||||
art_names = [e.get("name") for e in art.get("entries", [])]
|
||||
if len(art_names) != len(set(art_names)):
|
||||
errs.append("duplicate entry names in artifact")
|
||||
if set(art_names) != set(disk_names):
|
||||
missing = sorted(set(disk_names) - set(art_names))
|
||||
extra = sorted(set(art_names) - set(disk_names))
|
||||
errs.append("name set mismatch; missing=%s extra=%s" % (missing, extra))
|
||||
|
||||
live_counts = recompute()
|
||||
disk_names = sorted(os.listdir(PROFILE))
|
||||
live_files = sum(1 for n in disk_names if not os.path.isdir(os.path.join(PROFILE, n)))
|
||||
live_dirs = len(disk_names) - live_files
|
||||
if art.get("counts") != {"file": live_files, "dir": live_dirs}:
|
||||
errs.append("counts mismatch: artifact=%r live=%r"
|
||||
% (art.get("counts"), {"file": live_files, "dir": live_dirs}))
|
||||
if art.get("file_classes") != live_counts:
|
||||
errs.append("file_classes mismatch: artifact=%r live=%r" % (art.get("file_classes"), live_counts))
|
||||
if art.get("total_entries") != len(disk_names):
|
||||
errs.append("total_entries mismatch: %r" % art.get("total_entries"))
|
||||
|
||||
for e in art.get("entries", []):
|
||||
if e.get("class") not in VOCAB:
|
||||
errs.append("unknown class label %r" % (e.get("class"),))
|
||||
want = dir_rule(e["name"]) if os.path.isdir(os.path.join(PROFILE, e["name"])) else suffix_rule(e["name"])
|
||||
if want != e.get("class"):
|
||||
errs.append("class mismatch %s: artifact=%r recompute=%r" % (e["name"], e.get("class"), want))
|
||||
|
||||
if errs:
|
||||
print("CHECK FAIL")
|
||||
for e in errs:
|
||||
print(" -", e)
|
||||
return 1
|
||||
|
||||
files = sum(1 for n in disk_names if not os.path.isdir(os.path.join(PROFILE, n)))
|
||||
dirs = len(disk_names) - files
|
||||
print("CHECK OK: %d entries (%d files, %d dirs); artifact == independently "
|
||||
"recomputed from live profile disk" % (len(disk_names), files, dirs))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user