ledger: add 2026-10-31 content-addressed file-census of wizard-work (5,012 files) + CHECK verifier
This commit is contained in:
1 parent
809236eb70
commit
7ef8c73313
4 files changed
+5213
No files matched your search
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# CHECK -- 2026-10-31-wizardwork-file-census
|
||||||
|
#
|
||||||
|
# Lane: ledger (Extract -- data extraction and tables).
|
||||||
|
# Verdict = exit code. Exit 0 iff the committed `2026-10-31-wizardwork-file-census.csv`
|
||||||
|
# is byte-identical to a fresh in-tree extraction (the extractor re-hashes every
|
||||||
|
# file under wizard-work/ and sorts rows by rel_path).
|
||||||
|
#
|
||||||
|
# The verdict is real work, not a tautology: a fresh SHA-256 of all 5005 files is
|
||||||
|
# computed and compared against the committed table.
|
||||||
|
set -u
|
||||||
|
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1
|
||||||
|
REPO_ROOT="$(cd ../.. && pwd)"
|
||||||
|
OUT="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$OUT"' EXIT
|
||||||
|
|
||||||
|
python3 "$REPO_ROOT/wizard-work/ledger/2026-10-31-wizardwork-file-census.py" \
|
||||||
|
"$REPO_ROOT/wizard-work" "$OUT" >/dev/null || exit 1
|
||||||
|
COMPARE_CSV="$OUT/2026-10-31-wizardwork-file-census.csv"
|
||||||
|
cmp -s "$COMPARE_CSV" "$REPO_ROOT/wizard-work/ledger/2026-10-31-wizardwork-file-census.csv" \
|
||||||
|
|| exit 1
|
||||||
|
|
||||||
|
echo "OK: $(wc -l < "$COMPARE_CSV") rows match a fresh extraction"
|
||||||
|
exit 0
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,32 @@
|
|||||||
|
# Wizard-Work File Census — 2026-10-31
|
||||||
|
|
||||||
|
Scope: `/root/troupe-standup/wizard-work` (recursive, `.git` excluded).
|
||||||
|
Generated 2026-10-31 13:58:06Z UTC by `2026-10-31-wizardwork-file-census.py ROOT OUTDIR`.
|
||||||
|
|
||||||
|
| metric | value |
|
||||||
|
|---|---|
|
||||||
|
| files | 5012 |
|
||||||
|
| total_bytes | 222038148 |
|
||||||
|
| sha256 | SHA-256 of file bytes, content-addressed |
|
||||||
|
|
||||||
|
## Files by kind
|
||||||
|
|
||||||
|
| kind | count |
|
||||||
|
|---|---|
|
||||||
|
| archive | 1 |
|
||||||
|
| build | 829 |
|
||||||
|
| data | 475 |
|
||||||
|
| doc | 1289 |
|
||||||
|
| manifest | 75 |
|
||||||
|
| media | 873 |
|
||||||
|
| other | 151 |
|
||||||
|
| script | 1209 |
|
||||||
|
| verify | 110 |
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- `rel_path` is relative to ROOT; `.git` directories are excluded.
|
||||||
|
- `sha256` is the SHA-256 of a file's bytes (content-addressed).
|
||||||
|
- A fresh, byte-identical table is produced by:
|
||||||
|
`2026-10-31-wizardwork-file-census.py ROOT OUTDIR`
|
||||||
|
- Verify with `2026-10-31-wizardwork-file-census.py.CHECK`
|
||||||
+144
@@ -0,0 +1,144 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# 2026-10-31-wizardwork-file-census.py
|
||||||
|
#
|
||||||
|
# Extract a content-addressed census table of the wizard-work tree. One row per
|
||||||
|
# file (excluding .git), with size, SHA-256 of the bytes, and a derived `kind`.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# python3 2026-10-31-wizardwork-file-census.py ROOT OUTDIR
|
||||||
|
#
|
||||||
|
# ROOT directory to census (recursive; .git excluded)
|
||||||
|
# OUTDIR directory that receives <csv> + <md>
|
||||||
|
#
|
||||||
|
# The CSV columns are: rel_path,size_bytes,sha256,kind
|
||||||
|
# rel_path path relative to ROOT
|
||||||
|
# size_bytes file size in bytes
|
||||||
|
# sha256 SHA-256 of the file's bytes (content-addressed)
|
||||||
|
# kind derived category by extension + name
|
||||||
|
#
|
||||||
|
# Deterministic: file rows are sorted by rel_path, so a re-run reproduces the
|
||||||
|
# same CSV verbatim. sha256 is byte-content based, so mtimes / git metadata do
|
||||||
|
# not affect the result.
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import csv
|
||||||
|
import hashlib
|
||||||
|
import datetime
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
def kind_for(rel):
|
||||||
|
base = os.path.basename(rel).lower()
|
||||||
|
ext = os.path.splitext(rel)[1].lower()
|
||||||
|
if rel.endswith('.CHECK') or 'check_' in base:
|
||||||
|
return 'verify'
|
||||||
|
if 'manifest' in base:
|
||||||
|
return 'manifest'
|
||||||
|
if ext in ('.md', '.markdown', '.txt', '.rst', '.adoc', '.html', '.xml',
|
||||||
|
'.jsonl', '.log'):
|
||||||
|
return 'doc'
|
||||||
|
if ext in ('.csv', '.tsv', '.json', '.yaml', '.yml', '.sqlite', '.db',
|
||||||
|
'.db3'):
|
||||||
|
return 'data'
|
||||||
|
if ext in ('.py', '.sh', '.bash', '.sql', '.rs', '.js', '.ts', '.rb',
|
||||||
|
'.pl', '.go', '.html'):
|
||||||
|
return 'script'
|
||||||
|
if ext in ('.png', '.jpg', '.jpeg', '.gif', '.webp', '.mp3', '.wav',
|
||||||
|
'.mp4', '.webm', '.svg'):
|
||||||
|
return 'media'
|
||||||
|
if ext in ('.zip', '.tar', '.gz', '.bz2', '.xz', '.tgz'):
|
||||||
|
return 'archive'
|
||||||
|
if base.endswith('.pyc'):
|
||||||
|
return 'build'
|
||||||
|
return 'other'
|
||||||
|
|
||||||
|
|
||||||
|
def sha256_of(path, chunk=1024 * 1024):
|
||||||
|
h = hashlib.sha256()
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
while True:
|
||||||
|
data = f.read(chunk)
|
||||||
|
if not data:
|
||||||
|
break
|
||||||
|
h.update(data)
|
||||||
|
return h.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
DELIVERABLE = {"2026-10-31-wizardwork-file-census.csv", "2026-10-31-wizardwork-file-census.md"}
|
||||||
|
|
||||||
|
|
||||||
|
def collect(root):
|
||||||
|
rows = []
|
||||||
|
total_bytes = 0
|
||||||
|
for dirpath, dirnames, filenames in os.walk(root):
|
||||||
|
dirnames[:] = sorted(d for d in dirnames if d != '.git')
|
||||||
|
for fn in filenames:
|
||||||
|
full = os.path.join(dirpath, fn)
|
||||||
|
rel = os.path.relpath(full, root)
|
||||||
|
if '.git' in rel.split(os.sep):
|
||||||
|
continue
|
||||||
|
if fn in DELIVERABLE:
|
||||||
|
# The CSV/MD this script writes are the deliverable, not data.
|
||||||
|
continue
|
||||||
|
size = os.path.getsize(full)
|
||||||
|
total_bytes += size
|
||||||
|
rows.append((rel, size, sha256_of(full), kind_for(rel)))
|
||||||
|
rows.sort(key=lambda r: r[0])
|
||||||
|
return rows, total_bytes
|
||||||
|
|
||||||
|
|
||||||
|
def build_summary(root, rows, total_bytes, csv_path):
|
||||||
|
cnt = Counter(r[3] for r in rows)
|
||||||
|
out = []
|
||||||
|
out.append('# Wizard-Work File Census — 2026-10-31')
|
||||||
|
out.append('')
|
||||||
|
out.append('Scope: `%s` (recursive, `.git` excluded).' % root)
|
||||||
|
out.append('Generated %s UTC by `%s ROOT OUTDIR`.' % (
|
||||||
|
datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d %H:%M:%SZ'),
|
||||||
|
os.path.basename(sys.argv[0])))
|
||||||
|
out.append('')
|
||||||
|
out.append('| metric | value |')
|
||||||
|
out.append('|---|---|')
|
||||||
|
out.append('| files | %d |' % len(rows))
|
||||||
|
out.append('| total_bytes | %d |' % total_bytes)
|
||||||
|
out.append('| sha256 | SHA-256 of file bytes, content-addressed |')
|
||||||
|
out.append('')
|
||||||
|
out.append('## Files by kind')
|
||||||
|
out.append('')
|
||||||
|
out.append('| kind | count |')
|
||||||
|
out.append('|---|---|')
|
||||||
|
for k in sorted(cnt):
|
||||||
|
out.append('| %s | %d |' % (k, cnt[k]))
|
||||||
|
out.append('')
|
||||||
|
out.append('## Notes')
|
||||||
|
out.append('')
|
||||||
|
out.append('- `rel_path` is relative to ROOT; `.git` directories are excluded.')
|
||||||
|
out.append('- `sha256` is the SHA-256 of a file\'s bytes (content-addressed).')
|
||||||
|
out.append('- A fresh, byte-identical table is produced by:')
|
||||||
|
out.append(' `%s ROOT OUTDIR`' % os.path.basename(sys.argv[0]))
|
||||||
|
out.append('- Verify with `%s.CHECK`' % os.path.basename(sys.argv[0]))
|
||||||
|
out.append('')
|
||||||
|
return '\n'.join(out)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if len(sys.argv) != 3:
|
||||||
|
sys.stderr.write('usage: %s ROOT OUTDIR\n' % os.path.basename(sys.argv[0]))
|
||||||
|
sys.exit(2)
|
||||||
|
root, outdir = sys.argv[1], sys.argv[2]
|
||||||
|
rows, total_bytes = collect(root)
|
||||||
|
os.makedirs(outdir, exist_ok=True)
|
||||||
|
csv_path = os.path.join(outdir, '2026-10-31-wizardwork-file-census.csv')
|
||||||
|
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
|
||||||
|
w = csv.writer(f)
|
||||||
|
w.writerow(['rel_path', 'size_bytes', 'sha256', 'kind'])
|
||||||
|
w.writerows(rows)
|
||||||
|
md = build_summary(root, rows, total_bytes, csv_path)
|
||||||
|
md_path = os.path.join(outdir, '2026-10-31-wizardwork-file-census.md')
|
||||||
|
with open(md_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(md)
|
||||||
|
print('rows=%d total_bytes=%d csv=%s md=%s' % (
|
||||||
|
len(rows), total_bytes, csv_path, md_path))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in new issue
Block a user