AtlatestRepositoryfolio
1
#!/bin/sh2
# f4a-estate-scale.sh -- NOT A GATE. A scale and shape measurement of3
# `(folio graph)` against a COPY of the live Ops knowledge base.4
#5
# It is deliberately not a gate arm: its corpus is the live estate, which6
# changes daily, so every number it prints is true of one moment and no7
# assertion made against it would be reproducible. `test/gate-f4a.sh` owns the8
# reproducible claims over a frozen 12-file fixture corpus; this script answers9
# the different question of whether the fixture corpus is REPRESENTATIVE and10
# whether the thing survives two thousand real notes.11
#12
# THE LIVE ESTATE IS NEVER WRITTEN TO. Everything happens in a `/tmp` copy.13
# The safety verdict at the bottom asserts what it can actually establish -- the14
# copy is intact, and every change under $SRC during the run is LISTED so a15
# reader can see it was somebody else's note-taking -- rather than refusing to16
# speak because a live corpus changed while being read. See the long comment17
# there for why the original before/after-hash version cried wolf on every run.18
#19
# Usage: sh test/f4a-estate-scale.sh [source-folio-root]21
set -u22
repo_dir=$(cd "$(dirname "$0")/.." && pwd)23
cd "$repo_dir" || exit 125
SRC="${1:-/home/daviwil/Ops/folio}"26
sigil="${SIGIL:-sigil}"27
export SIGIL_REDIRECTS="${SIGIL_F4A_REDIRECTS:-$repo_dir/dev-redirects.sgl}"29
[ -d "$SRC/notes" ] || { echo "SETUP-FAILED: no notes directory under $SRC"; exit 3; }30
[ -d "$repo_dir/build/dev/lib" ] || { echo "SETUP-FAILED: run \`sigil build\` first"; exit 3; }32
work=$(mktemp -d /tmp/f4a-estate.XXXXXX) || { echo "SETUP-FAILED: no temp dir"; exit 3; }33
trap 'rm -rf "$work"' EXIT INT TERM35
# Fingerprint = EVERY entry under the source -- files, directories and symlinks36
# -- with its type, size and mtime. It moves if anything is created, deleted,37
# truncated, rewritten or re-linked.38
#39
# `-type f` alone was the first version and it had a hole an adversarial review40
# demonstrated: adding a directory AND a symlink left the digest byte-identical.41
fingerprint () {42
find "$SRC" -printf '%y %P %s %T@\n' 2>/dev/null | sort | sha256sum | cut -d' ' -f143
}45
# A FLOOR ON THE FINGERPRINT ITSELF, and it is the load-bearing line here.46
#47
# `find` without `-printf` (non-GNU), a missing `sha256sum`, or an unreadable48
# `$SRC` all produce the EMPTY-INPUT digest e3b0c442…52b855 -- on BOTH sides.49
# Two empty digests compare equal, so the script would print "source untouched"50
# and exit 0 having measured nothing at all. That is the exact shape this whole51
# estate keeps paying for, so the fingerprint has to prove it saw something.52
EMPTY_SHA=e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b85553
fingerprint_entries () { find "$SRC" -printf '.\n' 2>/dev/null | wc -l | tr -d ' '; }55
before=$(fingerprint)56
entries=$(fingerprint_entries)57
if [ "$before" = "$EMPTY_SHA" ] || [ "${entries:-0}" -lt 100 ]; then58
echo "SETUP-FAILED: the fingerprint saw $entries entries (digest $before)." >&259
echo " A fingerprint over nothing compares equal to itself and would certify" >&260
echo " 'source untouched' without having looked. Check that \$SRC is readable" >&261
echo " and that find supports -printf." >&262
exit 363
fi65
W="$work/ws"66
mkdir -p "$W"67
copy_taken=$(date +%s)68
cp -r "$SRC/notes" "$W/notes" || { echo "SETUP-FAILED: copy failed"; exit 3; }69
# A copied index database would make the first open report `intact` over a70
# schema this build may not share.71
find "$W" -name '.folio-*.db*' -delete 2>/dev/null73
# The exit status and the PROBE_DONE marker are BOTH checked, for the reason74
# `gate-f4a.sh` gives at length: piping `sigil` into `grep` makes the pipeline's75
# status grep's, so without these every measurement below could fail and this76
# script would still exit 0 having printed zeroes.77
probe () {78
po="$work/probe.out"79
"$sigil" -L "$repo_dir/build/dev/lib" -L "$repo_dir/src" \80
"$repo_dir/tools/f4a_graph_probe.sgl" "$@" > "$po" 2>&181
rc=$?82
if [ "$rc" -ne 0 ] || ! grep -q '^PROBE_DONE' "$po"; then83
echo "PROBE FAILED (rc=$rc, no completion marker): $*" >&284
grep -v '\[INFO\]' "$po" | tail -8 >&285
return 186
fi87
grep -v '\[INFO\]' "$po"88
}89
field () { awk -F'\t' -v n="$2" '$1 == v { print $n }' v="$1"; }91
echo "=========================================================================="92
echo "f4a estate scale measurement -- NOT A GATE"93
echo "=========================================================================="94
echo "source : $SRC (read-only; work happens in $W)"95
echo "compiler : $("$sigil" --version 2>&1 | sed 's/\x1b\[[0-9;]*m//g' | tail -1)"96
echo "files : $(find "$W/notes" -type f | wc -l | tr -d ' ') under notes/"97
echo " .md : $(find "$W/notes" -name '*.md' | wc -l | tr -d ' ')"98
echo " .sgd : $(find "$W/notes" -name '*.sgd' | wc -l | tr -d ' ')"99
echo101
start=$(date +%s)102
probe rebuild "$W" ops file > "$work/rebuild.txt" || { echo "REBUILD FAILED"; cat "$work/rebuild.txt"; exit 1; }103
end=$(date +%s)104
docs=$(field DOCS 2 < "$work/rebuild.txt")105
echo "rebuild : $docs documents in $((end - start))s"107
probe edges "$W" ops file > "$work/edges.txt"108
probe nodes "$W" ops file > "$work/nodes.txt"109
probe diagnostics "$W" ops file > "$work/diags.txt"110
probe orphans "$W" ops file > "$work/orphans.txt"111
probe unresolved "$W" ops file > "$work/unres.txt"112
probe cycles "$W" ops file > "$work/cycles.txt"114
echo "nodes : $(field NODE_COUNT 2 < "$work/nodes.txt")"115
echo "edges : $(field EDGE_COUNT 2 < "$work/edges.txt")"116
echo " by kind :"117
awk -F'\t' '$1 == "EDGE" { print $3 }' "$work/edges.txt" | sort | uniq -c | sort -rn | sed 's/^/ /'118
echo "resolution :"119
awk -F'\t' '$1 == "EDGE" { print $11 }' "$work/edges.txt" | sort | uniq -c | sort -rn | sed 's/^/ /'120
echo "unresolved : $(field UNRESOLVED_COUNT 2 < "$work/unres.txt") declared edges"121
echo "orphans : $(field ORPHAN_COUNT 2 < "$work/orphans.txt") of $docs documents"122
echo "diagnostics: $(field DIAGNOSTIC_COUNT 2 < "$work/diags.txt")"123
awk -F'\t' '$1 == "DIAGNOSTIC" { print $5 }' "$work/diags.txt" | sort | uniq -c | sort -rn | sed 's/^/ /'124
echo "cycles : $(field CYCLE_COUNT 2 < "$work/cycles.txt")"125
grep '^CYCLE ' "$work/cycles.txt" | head -5 | sed 's/^/ /'126
echo128
# CONVERGENCE ON REAL DATA. The gate proves this over 12 curated files; this129
# asks whether it survives the real corpus's shapes.130
#131
# IT DRIVES A REAL INCREMENTAL PASS FIRST. The earlier version compared a132
# rebuild against a rebuild, which is a determinism check wearing a convergence133
# check's label -- it could not answer the question its own comment asked. An134
# adversarial review caught the mislabel. Re-indexing every source through the135
# incremental entry point and THEN rebuilding is the comparison that matters.136
echo "convergence: re-indexing incrementally over the whole corpus, then rebuilding..."137
inc_start=$(date +%s)138
inc_failed=0139
# ITERATE THE CENSUS, NOT THE FILESYSTEM. `find -type f` under a real estate140
# turns up things discovery deliberately skips -- `.wrangler/cache/pages.json`141
# and friends -- and `graph-index-document!` correctly REFUSES a path that is142
# not a discoverable source. The first version of this loop fed it every file143
# and collected raises, which was this script being wrong about the product144
# rather than the product being wrong.145
probe sources "$W" ops file | awk -F'\t' '$1 == "SOURCE" { print $2 }' | sort > "$work/sources.txt"146
census_n=$(wc -l < "$work/sources.txt" | tr -d ' ')148
# THE INCREMENTAL PASS IS SAMPLED, AND THE SAMPLE IS STATED.149
#150
# `graph-index-document!` runs a full census on every call -- that is what makes151
# it converge, because the two whole-corpus facts (is this slug contested, does152
# this document still exist) cannot be derived from one file. The cost is that153
# incremental indexing is O(corpus) PER CALL, so re-indexing all 2,900 notes one154
# at a time is O(n^2) and does not finish in a useful time.155
#156
# That is a real property worth knowing rather than hiding, so it is measured157
# and reported: a bounded sample with a denominator beats an unbounded pass that158
# never returns. Override with F4A_ESTATE_SAMPLE=0 for all of them.159
sample=${F4A_ESTATE_SAMPLE:-200}160
if [ "$sample" -gt 0 ] && [ "$census_n" -gt "$sample" ]; then161
head -n "$sample" "$work/sources.txt" > "$work/sample.txt"162
echo " census: $census_n sources; SAMPLING $sample for the incremental pass"163
echo " (graph-index-document! runs a full census per call, so a whole-corpus"164
echo " incremental pass is O(n^2); the rebuild above is the O(n) path)"165
else166
cp "$work/sources.txt" "$work/sample.txt"167
echo " census: $census_n sources; indexing ALL of them incrementally"168
fi169
while IFS= read -r f; do170
probe index "$W" ops file "$f" > /dev/null || { inc_failed=$((inc_failed + 1)); }171
done < "$work/sample.txt"172
inc_end=$(date +%s)173
echo " incremental pass: $(wc -l < "$work/sample.txt" | tr -d " ") documents in $((inc_end - inc_start))s, $inc_failed failure(s)"174
probe dump "$W" ops file | grep '^R ' | cut -f2- > "$work/a.txt"175
probe rebuild "$W" ops file > /dev/null176
probe dump "$W" ops file | grep '^R ' | cut -f2- > "$work/b.txt"177
la=$(wc -l < "$work/a.txt" | tr -d ' '); lb=$(wc -l < "$work/b.txt" | tr -d ' ')178
if [ "$la" -eq 0 ] || [ "$lb" -eq 0 ]; then179
echo " INCONCLUSIVE: a dump was empty ($la / $lb); two empty dumps compare equal"180
elif diff -q "$work/a.txt" "$work/b.txt" > /dev/null 2>&1; then181
echo " incremental-vs-rebuild IDENTICAL over $la relation lines"182
else183
echo " DIVERGED over $la/$lb relation lines:"184
diff -u "$work/a.txt" "$work/b.txt" | head -20 | sed 's/^/ /'185
fi187
# ==========================================================================188
# THE SAFETY VERDICT189
# ==========================================================================190
#191
# WHAT THIS SCRIPT ACTUALLY CLAIMS: it never writes under $SRC. The first192
# version tried to establish that with a before/after fingerprint of the source193
# tree, and refused to report its numbers if the fingerprint moved.194
#195
# THAT GUARD FIRED ON EVERY RUN, and it was RIGHT about the fact and WRONG about196
# the conclusion. $SRC is a live knowledge base that four agents write to197
# continuously; during one two-hour run, 24 files changed -- notes, projects,198
# inbox, the FTS index -- written by two other workers, the leader, and this199
# worker's own note-taking. NONE of them was anywhere this script writes.200
#201
# The expectation was wrong, not the instrument: it assumed a QUIESCENT source,202
# and that assumption was never written down so it was never checked. A203
# before/after hash CANNOT ATTRIBUTE A CHANGE TO A WRITER, so it cannot answer204
# the question this script is actually asking.205
#206
# And a guard that cries wolf on every run is worse than no guard: a false red207
# that fires occasionally gets investigated, one that fires ALWAYS gets a208
# learned exception -- and the exception is indistinguishable from the real209
# detection it was built for. See210
# [[topics/a-wrong-expectation-is-a-false-red]].211
#212
# SO IT NOW ASSERTS WHAT IT CAN, AND REPORTS WHAT IT CANNOT:213
#214
# 1. the /tmp COPY is intact and non-empty -- a real precondition215
# 2. no path under $SRC is newer than the copy -- ATTRIBUTED, see below216
# 3. foreign writes during the window are REPORTED, not fatal217
#218
# Check 2 is the one that carries the claim. Every file this script could219
# possibly have written would have to be newer than the moment the copy was220
# taken AND lie under $SRC. Listing those explicitly turns "something changed"221
# into "here is exactly what changed, decide for yourself" -- which is the222
# print-the-list-not-the-count lesson from the same night.223
after=$(fingerprint)224
echo226
copy_files=$(find "$W/notes" -type f 2>/dev/null | wc -l | tr -d ' ')227
if [ "${copy_files:-0}" -lt 100 ]; then228
echo "SETUP-FAILED: the working copy holds only $copy_files files; nothing above was measured"229
exit 3230
fi231
echo "working copy : $copy_files files, intact"233
if [ "$before" = "$after" ]; then234
echo "source : fingerprint unchanged ($before)"235
echo236
echo "SAFE: no writes to $SRC, and no foreign writes during the window."237
exit 0238
fi240
# The fingerprint moved. Say WHAT moved rather than refusing to speak.241
echo "source : fingerprint MOVED ($before -> $after)"242
echo " $SRC is a LIVE corpus; other agents write to it continuously."243
echo244
echo "changed under $SRC during this run (newer than the copy):"245
find "$SRC" -newermt "@$copy_taken" -type f 2>/dev/null \246
| sed "s|^$SRC/| |" | sort | head -40247
n=$(find "$SRC" -newermt "@$copy_taken" -type f 2>/dev/null | wc -l | tr -d ' ')248
echo " ($n file(s))"249
echo250
echo "FOREIGN WRITES DETECTED; THE NUMBERS ABOVE DESCRIBE THE SNAPSHOT."251
echo " This script writes only under /tmp. If every path listed above is an"252
echo " ordinary folio write (notes/, projects/, inbox.md, .folio-index.db*),"253
echo " that is another agent using the knowledge base and the measurements"254
echo " stand -- they ran against a copy taken before those writes."255
echo " If any listed path is one THIS script could have produced, stop and"256
echo " investigate: that would be the thing the check exists for."257
exit 0