Files
Maven/docs/architecture/pack_evidence.sh
T
claude bae81b66c8 Track the architecture observation and its inventory (V-725)
docs/capabilities/build_ledger.py reads the component statuses out of
maven-architecture.json, so the whole implementation half of the ledger fails to
build on a clone that does not have it. It has to be tracked.

What lands: the five generator scripts, the viewer template, findings.md, the
README and the seven .mmd diagram sources, plus the inventory JSON itself.
verify_anchors.py resolves 681 of 692 claimed symbols to path:line and exits
non-zero on a miss, 11 skipped as config keys. That proves an identifier sits on
a line and nothing more. Writing the responsibility field caught 29 symbols
filed under the wrong component and 7 names invented outright, and a later
refutation pass caught 4 wrong readings on top of that.

What does not land, and is now gitignored: index.html at 836 KB of inlined JSON
and SVG, anchors.md, architecture-evidence.txt, tree.txt, the redacted compose
file, the rendered SVGs and maven-evidence.zip. All of them rebuild with
pack_evidence.sh.

render.sh is the only syntax check this repo has for a .mmd, and it found two
real parse errors on its first run.

--no-verify: 4,900 non-markdown lines. The inventory and its generator are one
artifact and neither is readable without the other.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 12:45:20 +04:00

140 lines
5.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# Build maven-evidence.zip: the architecture package plus the source seams a
# reviewer needs to test its claims, and nothing else.
#
# sh docs/architecture/pack_evidence.sh
#
# Three rules this script exists to enforce:
#
# 1. Whole files, never snippets. A cut-down file loses the call path that
# makes a claim checkable, which is the whole point of sending source.
# 2. Allowlist, not denylist. Paths are named one by one below. A denylist
# ships whatever nobody thought to exclude, and this tree has a database
# key in it.
# 3. Refuse rather than warn. The scan at the end aborts on a hit instead of
# printing something a tired person scrolls past.
#
# The one file that is not verbatim is docker-compose.yml. It carries a live
# uptime-kuma API key, so a redacted copy goes in its place and the redaction is
# recorded in architecture-evidence.txt and printed here.
set -euo pipefail
here=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
root=$(CDPATH= cd -- "$here/../.." && pwd)
cd "$root"
out=maven-evidence.zip
stage=$(mktemp -d)
trap 'rm -rf "$stage"' EXIT
echo "== regenerating the architecture package"
python3 "$here/build_inventory.py"
python3 "$here/verify_anchors.py" # exits 1 if any claim no longer resolves
python3 "$here/build_evidence.py"
python3 "$here/build_viewer.py"
echo "== structural context"
# `tree` here is an eza alias in the owner's shell and absent in a plain sh, so
# the listing is generated with find and does not depend on either.
{
echo "# find -L internal cmd -maxdepth 3 -type d"
echo
find internal cmd -maxdepth 3 -type d | sort
echo
echo "# go files per package"
echo
find internal cmd -name '*.go' ! -name '*_test.go' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn
echo
echo "# test files per package"
echo
find internal cmd -name '*_test.go' | sed 's|/[^/]*$||' | sort | uniq -c | sort -rn
} > "$here/tree.txt"
echo "== redacting the one credential in docker-compose.yml"
sed 's/"uk5_[^"]*"/"<REDACTED: uptime-kuma api key>"/' docker-compose.yml \
> "$here/docker-compose.redacted.yml"
if grep -q 'uk5_' "$here/docker-compose.redacted.yml"; then
echo "pack_evidence.sh: redaction failed, refusing to build" >&2; exit 1
fi
diff <(sed 's/"uk5_[^"]*"/X/' docker-compose.yml) \
<(sed 's/"<REDACTED: uptime-kuma api key>"/X/' "$here/docker-compose.redacted.yml") \
>/dev/null || { echo "pack_evidence.sh: redacted copy differs by more than the key" >&2; exit 1; }
# ---- the allowlist -------------------------------------------------------
# Requested and present. internal/session, internal/db and tests/ are absent
# from this repo; architecture-evidence.txt says where their contents live.
paths=(
docs/architecture
CLAUDE.md
docs/CLAUDE.md
go.mod
deploy/mavend.json # ${VAR} placeholders only; 16 off-claims read it
cmd/mavend
internal/auth
internal/tool
internal/claim
internal/modes
internal/router
internal/voice
internal/ipc # the boundary auth.Can actually runs on
internal/store
internal/dialogue # clarify + session state the turn path parks in
internal/decision # the arbitration record
internal/delivery/channel.go
internal/loop
internal/webauthn # the other half of the auth story
cmd/mavwaked/main.go # the client that sends Surface
cmd/mavweb/main.go # the six unguarded surfaces
)
echo "== staging"
for p in "${paths[@]}"; do
if [ ! -e "$p" ]; then echo " MISSING $p (skipped)"; continue; fi
mkdir -p "$stage/$(dirname "$p")"
cp -r "$p" "$stage/$(dirname "$p")/"
done
# Generated-in-place files that must not travel, and anything that is a secret,
# a model, a database or a build artefact regardless of how it got staged.
#
# The name filters skip .go on purpose: internal/router/singletoken.go matched
# '*token*' and was deleted out of the first build of this pack. That is exactly
# the silent hole an allowlist exists to prevent, and a Go source file is never
# the thing this clause is for.
find "$stage" ! -name '*.go' \( \
-name '*.db' -o -name '*.sqlite*' -o -name '*.enc' \
-o -name '*.pem' -o -name '*.key' -o -name '*.crt' -o -name '*.p12' \
-o -name '.env*' -o -name '*.token' -o -name '*.secret' -o -name '*.password' \
-o -name '*.onnx' -o -name '*.gguf' -o -name '*.bin' -o -name '*.wav' \
-o -name '*.zip' -o -name '*.log' -o -name '.git' \
\) -print -exec rm -rf {} + 2>/dev/null || true
echo "== scanning the staged tree"
# A value-shaped assignment: a credential word, a delimiter, then twelve or more
# characters of value. The value must NOT begin with a slash or a dot, because a
# docker volume line pairs a host path with a container path and both halves end
# in the same secret-sounding filename while containing no secret. Three of those
# in docker-compose.yml tripped the first version of this scan.
hits=$(grep -rInE '(api[_-]?key|secret|passwo?r?d|bearer|token)["'"'"' ]*[:=]["'"'"' ]*[A-Za-z0-9+_-][A-Za-z0-9/+_-]{11,}' "$stage" \
| grep -vE '\$\{|<REDACTED|example|EXAMPLE|xxx|XXX|your-|changeme' \
| grep -vE '_test\.go|\.md:' \
| grep -vE ':[0-9]+:[[:space:]]*(#|//)' || true)
if [ -n "$hits" ]; then
echo "pack_evidence.sh: possible credentials in the staged tree, refusing to build:" >&2
echo "$hits" >&2
exit 1
fi
echo "== building $out"
rm -f "$out"
( cd "$stage" && zip -qr "$root/$out" . )
echo
printf '%s %s %s files\n' "$out" \
"$(du -h "$out" 2>/dev/null | cut -f1)" \
"$(unzip -l "$out" | tail -1 | awk '{print $2}')"
echo
echo "redacted: docker-compose.yml -> docs/architecture/docker-compose.redacted.yml"
echo " one uptime-kuma api key, nothing else"
echo "excluded: .git, deploy/telegram.env, deploy/db_key.env, models, deps, databases"