fix(manifest): fold in both cross-frontier panels — and a live hole in v0.2.2
Two four-arm artifact-only rounds landed together: the contract paraphrase (against the pre-seam-review capture) and the code-vs-contract conformance review (against the amended one), correctly firewalled from each other. The conformance round found ZERO drift in the strict sense — the code is a clause-for-clause implementation of the contract — and the weight of both rounds landed one layer down, in what green tests structurally cannot report. Full triage in persistent-memory.d/. A LIVE HOLE IN RELEASED CODE, FOUND ON THE SIBLING MODULE v0.2.2 adopted the RecursionError finding from the bug-hunt round and closed half of it: `_hydrate_safe` guards hydration, but `json.loads` runs above it in `_read_raw`, whose catch list covers neither RecursionError nor MemoryError. A 400 KB file of nothing but brackets in any ONE booth therefore still returned 500 for `/` and `/healthz` across every booth on the service. Confirmed by running it before believing it. Both modules now bound the read by `stat` before touching the bytes and catch both classes anyway, so raising a bound later cannot quietly re-open the hole. The strict half of the marks asymmetry refuses everything the lenient half tolerates, or a file that reads as "no marks" gets replaced by a write that believed it. THE WHY-WIPE `booth new x --why "..."` then `booth add x out/*.png` erased the sentence the first command existed to record. Omitted flags meant empty strings and empty strings overwrote. Two arms predicted it from the contract's wording alone; every test here passed --why on both calls and so could not see it. Omitted now means unchanged and an explicit --why "" still clears — the shell carries the distinction by leaving the variable UNSET, not empty. --title WAS WRITE-ONLY Stored, flag-surfaced, rendered nowhere. 4/4, and independently top-ranked by every arm of the paraphrase round. It lands on the booth page heading with the directory name beside it, because the directory name is the identity the operator navigates by and refers to positionally. THREE TESTS THAT COULD NOT FAIL - test_the_write_is_atomic asserted no *.tmp survived, which a plain write_text passes. It asserts the inode changes now. (The first replacement was ALSO vacuous — it spied on os.open, which Path.write_text reaches through io.open in C and never touches. Recorded in the test, because writing a second vacuous test while fixing the first is exactly the failure this round is about.) - The INV-3 preservation test passed against an implementation that regenerated `created` every time, because _now() is whole-second resolution and back-to-back writes share a stamp. Seeded from 2019 now. - test_announcing_is_activity passed whether or not _newest_mtime counted the manifest, because writing it bumps the directory mtime either way. The directory's clock is put back, leaving the file as the only thing that can keep the booth alive. ALSO - The title fallback skipped the normalizer the explicit value gets; a directory name may legally carry a newline and run to 255 bytes. - Every writer derived the same .booth.json.tmp. Marks are protected from that by their flock; the manifest has none, so uniqueness stands in. - test_stdlib_only was blind to relative imports in all four modules. - INV-1 had no guard at all; INV-5 named two different promises; the negative render states were asserted on the index only. Contract amended throughout: the 4 GB case is a stat-checked bound rather than a return constraint, every field of an error-carrying record has a stated value, INV-1 no longer contradicts INV-3, repo-wide rules are named in words instead of by a colliding number, and touches admits the macro partial the implementation added. 329 tests.
This commit is contained in:
+27
-13
@@ -103,15 +103,20 @@ LINKS_BOARD="${BOOTH_LINKS_BOARD:-links}"
|
||||
# appear, so `booth add b *.png --why "..."` and `booth add b --why "..." *.png`
|
||||
# both work — a glob is usually last and a flag usually after it, but nothing
|
||||
# enforces that and a session should not have to care.
|
||||
WHY=""; TITLE=""; ARGS=()
|
||||
# OMITTED IS NOT EMPTY. `booth new x --why "..."` then `booth add x out/*.png`
|
||||
# is the ordinary sequence, and while an omitted flag meant "" the second
|
||||
# command silently erased the sentence the first one existed to record. So the
|
||||
# shell tracks WHETHER the flag was given, and only passes it on when it was —
|
||||
# an explicit `--why ""` still clears, which is a different intention.
|
||||
WHY=""; TITLE=""; WHY_SET=0; TITLE_SET=0; ARGS=()
|
||||
strip_announce_flags() {
|
||||
ARGS=()
|
||||
ARGS=(); WHY_SET=0; TITLE_SET=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--why) [ $# -ge 2 ] || usage; WHY="$2"; shift 2 ;;
|
||||
--title) [ $# -ge 2 ] || usage; TITLE="$2"; shift 2 ;;
|
||||
--why=*) WHY="${1#--why=}"; shift ;;
|
||||
--title=*) TITLE="${1#--title=}"; shift ;;
|
||||
--why) [ $# -ge 2 ] || usage; WHY="$2"; WHY_SET=1; shift 2 ;;
|
||||
--title) [ $# -ge 2 ] || usage; TITLE="$2"; TITLE_SET=1; shift 2 ;;
|
||||
--why=*) WHY="${1#--why=}"; WHY_SET=1; shift ;;
|
||||
--title=*) TITLE="${1#--title=}"; TITLE_SET=1; shift ;;
|
||||
*) ARGS+=("$1"); shift ;;
|
||||
esac
|
||||
done
|
||||
@@ -120,18 +125,27 @@ strip_announce_flags() {
|
||||
# Announce a booth. Goes through booth/manifest.py rather than printf-ing JSON
|
||||
# from the shell, because a why containing a quote, a backslash or a newline is
|
||||
# not an edge case — it is a sentence somebody wrote.
|
||||
# announce <dir> <handle> [title] [why] — the trailing two are passed as
|
||||
# environment variables that are UNSET when the flag was not given, because
|
||||
# that is the only way the shell can say "leave it alone" rather than "".
|
||||
announce() {
|
||||
BOOTH_SRC="$(cd "$(dirname -- "$(readlink -f -- "$0")")/.." && pwd)" \
|
||||
BOOTH_ANN_DIR="$1" BOOTH_ANN_HANDLE="$2" \
|
||||
BOOTH_ANN_TITLE="${3:-}" BOOTH_ANN_WHY="${4:-}" python3 -c '
|
||||
local -a envs
|
||||
envs=( "BOOTH_SRC=$(cd "$(dirname -- "$(readlink -f -- "$0")")/.." && pwd)"
|
||||
"BOOTH_ANN_DIR=$1" "BOOTH_ANN_HANDLE=$2" )
|
||||
[ "${TITLE_SET:-0}" = 1 ] && envs+=( "BOOTH_ANN_TITLE=${3:-}" )
|
||||
[ "${WHY_SET:-0}" = 1 ] && envs+=( "BOOTH_ANN_WHY=${4:-}" )
|
||||
env "${envs[@]}" python3 -c '
|
||||
import os, pathlib, sys
|
||||
sys.path.insert(0, os.environ["BOOTH_SRC"])
|
||||
try:
|
||||
from booth.manifest import write_manifest
|
||||
kw = {}
|
||||
# Absent means the flag was omitted; present-and-empty means it was given
|
||||
# as "" and the poster meant to take the line back.
|
||||
if "BOOTH_ANN_TITLE" in os.environ: kw["title"] = os.environ["BOOTH_ANN_TITLE"]
|
||||
if "BOOTH_ANN_WHY" in os.environ: kw["why"] = os.environ["BOOTH_ANN_WHY"]
|
||||
write_manifest(pathlib.Path(os.environ["BOOTH_ANN_DIR"]),
|
||||
os.environ["BOOTH_ANN_HANDLE"],
|
||||
title=os.environ["BOOTH_ANN_TITLE"],
|
||||
why=os.environ["BOOTH_ANN_WHY"])
|
||||
os.environ["BOOTH_ANN_HANDLE"], **kw)
|
||||
except Exception as exc:
|
||||
# A booth that could not announce itself is still a booth. Say so on stderr
|
||||
# and carry on: failing `booth add` over its metadata would lose the files
|
||||
@@ -247,7 +261,7 @@ case "$cmd" in
|
||||
# The board announces itself as the SERVICE's, not as any one agent's:
|
||||
# seventeen handles post to it, so no handle owns it. Idempotent — a second
|
||||
# link keeps the original creation stamp.
|
||||
announce "$board" "booth" "$LINKS_BOARD" \
|
||||
TITLE_SET=1 WHY_SET=1 announce "$board" "booth" "$LINKS_BOARD" \
|
||||
"the standing link board — every agent session posts here"
|
||||
# Provenance, because a bare URL is unreadable three days later: who posted
|
||||
# it, from where, and when.
|
||||
|
||||
Reference in New Issue
Block a user