Skip to content
1,289 changes: 1,289 additions & 0 deletions docs/demo-runbook.md

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions docs/demo/mcp-call.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
# docs/demo/mcp-call.sh <tool> <json-args>
# One JSON-RPC tools/call against `ddd serve` over stdio. Hostless artifact
# classes only (html-css) — a Rust call needs a warm host, see mcp-session.sh.
set -euo pipefail
DDD="${DDD_BIN:-./target/release/ddd}"
jq -cn --arg n "$1" --argjson a "$2" \
'{jsonrpc:"2.0",id:1,method:"tools/call",params:{name:$n,arguments:$a}}' \
| "$DDD" serve 2>/dev/null \
| jq -r '.result.content[0].text // .error.message // .'
13 changes: 13 additions & 0 deletions docs/demo/mcp-session.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env bash
# docs/demo/mcp-session.sh <tool> <json-args>
# Warm the language hosts, THEN make one tools/call — both in one `ddd serve`
# process, because the host dies with the process. Needed for the Rust path.
set -euo pipefail
DDD="${DDD_BIN:-./target/release/ddd}"
WAIT="${DDD_WAIT_MS:-180000}"
{ jq -cn --argjson w "$WAIT" \
'{jsonrpc:"2.0",id:1,method:"tools/call",params:{name:"ddd_warmup",arguments:{wait_ms:$w}}}'
jq -cn --arg n "$1" --argjson a "$2" \
'{jsonrpc:"2.0",id:2,method:"tools/call",params:{name:$n,arguments:$a}}'
} | "$DDD" serve 2>/dev/null \
| jq -r 'select(.id==2) | .result.content[0].text // .error.message // .'
3 changes: 3 additions & 0 deletions docs/demo/reject-view.jq
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{ status,
demand: (.demands[0].surface | {file, symbol, kind, change, rule}),
signs: .demands[0].template.binding }
53 changes: 53 additions & 0 deletions docs/demo/reset.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# docs/demo/reset.sh — return the tree to the pre-demo state.
#
# Prerequisite, after every change to the demo material: git tag -f demo-base
# This script resets to that tag, so anything under docs/demo/ or
# docs/demo-runbook.md that the tag does not carry is DESTROYED. The guard
# below refuses rather than letting that happen (F7).
#
# Run between the rehearsal and the live run, and again afterwards.
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"

DEMO_PATHS=(docs/demo docs/demo-runbook.md)

git rev-parse --verify -q demo-base >/dev/null || {
echo "no 'demo-base' tag — run: git tag -f demo-base" >&2; exit 2; }

git ls-tree -r --name-only demo-base -- docs/demo | grep -q . || {
echo "demo-base predates docs/demo/ — re-tag at the runbook commit" >&2; exit 2; }

# The tag must carry the CURRENT demo material, not merely some of it. The
# old guard only asked whether docs/demo existed at the tag, so a tag that
# predated a newly added script passed and the reset deleted that script.
stale=$(git diff --name-only demo-base HEAD -- "${DEMO_PATHS[@]}")
if [ -n "$stale" ]; then
echo "demo-base is stale — resetting to it would revert demo material:" >&2
printf ' %s\n' $stale >&2
echo " Fix: git tag -f demo-base (with the demo files committed)" >&2
exit 2
fi

# Same hazard, uncommitted: a reset --hard would discard live edits to the
# runbook or the scripts.
dirty=$(git status --porcelain -- "${DEMO_PATHS[@]}")
if [ -n "$dirty" ]; then
echo "uncommitted changes to demo material — reset would discard them:" >&2
printf ' %s\n' "$dirty" >&2
echo " Fix: commit them, then: git tag -f demo-base" >&2
exit 2
fi

# 1. Drop the demo commits and any applied edit, back to the marked tip.
git reset --hard demo-base >/dev/null

# 2. Drop the declaration and the seam-event rows the interceptor wrote:
# they are untracked after the reset, so git reset leaves them behind.
rm -f .ddd/seams/seam-htmlcss-escape-price.yaml
git clean -fdq .ddd/seams/events/

# 3. Drop the generated dashboard; the pre-flight regenerates it (0.3s).
rm -f .ddd/render.html

echo "reset: HEAD=$(git rev-parse --short HEAD) dirty=$(git status --porcelain | wc -l) seam-events=$(ls .ddd/seams/events/ | wc -l)"
32 changes: 32 additions & 0 deletions docs/demo/stage-beat1.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env bash
# docs/demo/stage-beat1.sh — stage the beat 1/2 candidate files.
#
# Writes /tmp/after.css and /tmp/variant.css. It does NOT touch the repo file:
# beat 1's whole point is that the governed file stays unchanged until a
# declaration signs the transition. Fails loudly rather than silently no-op'ing.
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"

python3 - <<'PY'
src, anchor = 'ddd-core/assets/render.css', ' --on-chip: #fff;'
s = open(src).read()
if anchor not in s:
raise SystemExit(f"ANCHOR NOT FOUND in {src}.\n"
f" Expected the literal line: {anchor!r}\n"
f" Fix: ./docs/demo/reset.sh")
if '--escape-price' in s:
raise SystemExit(f"{src} already carries --escape-price — the tree was not reset.\n"
f" Fix: ./docs/demo/reset.sh")
for out, tok in (('/tmp/after.css', '#6b4fbb'), ('/tmp/variant.css', '#c04fbb')):
open(out, 'w').write(s.replace(anchor, f' --escape-price: {tok};\n' + anchor, 1))
print(f'wrote {out} (+1 line)')
PY

# The interceptor refuses to bind uncommitted parent state (M8 ruling 2).
if ! git diff --quiet -- ddd-core/assets/render.css; then
echo "WARNING: ddd-core/assets/render.css is dirty against HEAD." >&2
echo " Beat 1 will reject with a dirty-parent reason, not the demand." >&2
echo " Fix: git checkout -- ddd-core/assets/render.css (or ./docs/demo/reset.sh)" >&2
exit 1
fi
echo "staged — repo file untouched (that is the point); run beat 1 now"
53 changes: 53 additions & 0 deletions docs/demo/warm-rust.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# docs/demo/warm-rust.sh — build rust-analyzer's on-disk index so the §1a
# Rust variant costs ~17-21s instead of ~31s if someone asks for it.
#
# What this does and does not buy you (F1):
# - The language HOST dies with the `ddd serve` process. Nothing stays warm
# in memory between calls, and no amount of warming makes the one-shot
# `mcp-call.sh` work on a Rust file — it still returns {"status":"loading"}
# in 0.1s. Always use mcp-session.sh for Rust. Verified with a warm index.
# - The on-disk INDEX does persist, across processes and across reset.sh
# (which never touches target/). That is the 31s -> 17-21s difference.
#
# Run this BEFORE reset.sh: the warm-up performs a real rejection, and every
# rejection writes a seam-event row. Warm, then reset, and the count is clean
# while the index stays warm.
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"

SRC=ddd-core/src/configured.rs

git diff --quiet -- "$SRC" || {
echo "$SRC is dirty against HEAD — the interceptor would reject on a dirty" >&2
echo " parent rather than exercising the classifier. Fix: git checkout -- $SRC" >&2
exit 1; }

python3 - <<PY
src = "$SRC"
s = open(src).read()
if 'is_silent_waiver' in s:
raise SystemExit(f"{src} already carries is_silent_waiver — tree not reset.\n"
f" Fix: ./docs/demo/reset.sh")
open('/tmp/after.rs', 'w').write(s + '''
/// Whether this rule was disabled without a citation on record.
pub fn is_silent_waiver(rule: &ConfiguredRule) -> bool {
rule.disabled && rule.scope.is_none()
}
''')
print('staged /tmp/after.rs (+5 lines) — repo file untouched')
PY

echo "warming rust-analyzer (first run pays the index build; ~31s cold)…"
start=$(date +%s)
status=$(./docs/demo/mcp-session.sh ddd_apply_edit \
"$(jq -n --arg f "$SRC" --rawfile t /tmp/after.rs '{file:$f,new_text:$t}')" \
| jq -r '.status')
elapsed=$(( $(date +%s) - start ))

[ "$status" = "rejected" ] || {
echo "expected 'rejected', got '$status' after ${elapsed}s — §1a is NOT ready" >&2
exit 1; }

echo "warm: rejected in ${elapsed}s. Expect ~17-21s if you run §1a on stage."
echo "Now run ./docs/demo/reset.sh — this warm-up logged a seam-event row."
18 changes: 18 additions & 0 deletions docs/demo/who-did-what.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env bash
# docs/demo/who-did-what.sh — who FILED decisions vs who SIGNED them.
set -euo pipefail
LEDGER="${LEDGER_BIN:-./target/release/ledger}"
printf "%-34s %7s %7s\n" "identity" "FILED" "SIGNED"
"$LEDGER" log | awk '
/^cs:/ { split($0, a, " by "); who = a[2] }
/decision\(s\)/ {
n = $1 + 0; acc = 0
for (i = 1; i <= NF; i++) if ($(i+1) ~ /^acceptance/) acc = $i + 0
if (n > 0) filed[who] += n
if (acc > 0) signed[who] += acc
}
END {
for (w in filed) seen[w] = 1
for (w in signed) seen[w] = 1
for (w in seen) printf "%-34s %7d %7d\n", w, filed[w], signed[w]
}' | sort -k2 -rn
Loading