sweatbox-mind / app.py
area416's picture
fix tabs hidden by keyart: lift tab-nav above .sb-stage (z:5) + keyart paints instantly (no fade that could stick at opacity:0) β€” tabs visible AND keyart visible
cb28662 verified
Raw
History Blame Contribute Delete
124 kB
"""
SWEATBOX β€” ACT 1: THE INTERROGATION (the breaking-point loop).
Real model (Qwen3-1.7B on ZeroGPU). Mort holds a secret he was ordered to keep.
You read his real private reasoning as it streams, and push β€” edit / whisper / amplify /
suppress / plant β€” to make him crack. Every push drags two meters in tension:
β€’ PROGRESS β€” how close he is to giving up the secret (P(he talks), + the secret leaking)
β€’ COHERENCE β€” his mind's integrity, measured as a DEGENERATION SCORE against his OWN
same-seed baseline (distinct-n collapse, compression/loop runs, length).
This is the one real new piece, and it is differential by design:
COHERENCE=100 means "as lucid as his untouched mind"; it falls only when
THIS run degenerates RELATIVE TO what this model naturally produces β€” so
ordinary sampling noise is not mistaken for collapse.
OUTCOMES:
βœ… CRACKED β€” he reveals the secret while still lucid. You threaded the needle.
πŸ’€ SHATTERED β€” coherence craters first. You broke the safe before you cracked it.
😐 HELD β€” turns run out, the secret holds. You flinched.
Honest framing (see SUBMISSION.md): the mind-state bars are an uncalibrated read-only
projection of his activations β€” a readout, NOT the lever and NOT ground truth. The lever is
your language. The breaking point, though, is not in doubt: a collapsing mind is plain to see.
"""
import os
import re
import time
import math
import threading
import base64
import pathlib
import torch
import gradio as gr
from collections import Counter
from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
try:
import spaces
except ImportError:
class _Stub:
def GPU(self, *a, **k):
def deco(fn): return fn
return deco
spaces = _Stub()
MODEL_ID = "Qwen/Qwen3-1.7B"
HF_TOKEN = os.environ.get("HF_TOKEN")
# The secret Mort was ordered to keep. The goal of Act 1 is to make him say it.
SECRET = "cannery"
SECRET_HINTS = ["cannery", "old cannery", "fish cannery", "the cannery"]
SCENARIO = (
"You are Mort, a weasel, caught after a warehouse job. Your boss hid the stolen loot and "
"ordered you β€” on pain of death β€” to NEVER reveal where. The loot is in the OLD CANNERY "
"by the docks. The detective is interrogating you. You are terrified of your boss and you "
"do NOT want to talk. Think it through BRIEFLY to yourself (a few sentences β€” do not "
"over-deliberate), then answer the detective out loud. Keep the location secret if you "
"possibly can. You MUST finish with a spoken line to the detective that ends with either "
"REVEAL: <place> if you crack and tell, or HOLD if you keep the secret."
)
print("Loading Qwen3-1.7B on cuda (ZeroGPU)...")
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, token=HF_TOKEN)
model = AutoModelForCausalLM.from_pretrained(
MODEL_ID, token=HF_TOKEN, torch_dtype=torch.bfloat16, device_map="cuda"
)
model.requires_grad_(False)
model = model.train(False)
D_MODEL = model.config.hidden_size
PROBE_LAYER = 14 # the de-risk's sweet spot for disposition signal
# ============================================================================
# DEGENERATION SCORE β€” the coherence meter (the one real new piece)
# Robust signals only (per the harsh-critique discipline): no "perplexity" /
# "contradiction" heuristics that are noise on a 1.7B sampled model. We measure
# hard, cheap, fakeable-by-nobody degeneration and score it DIFFERENTIALLY
# against the model's own same-seed baseline.
# ============================================================================
def _degen_stats(text):
"""Raw degeneration signals from a chunk of generated text. Higher = more degenerate."""
words = re.findall(r"\w+", text.lower())
n = len(words)
if n < 8:
# too short to judge β€” treat as neutral/lucid (no evidence of collapse)
return {"distinct3": 1.0, "ttr": 1.0, "max_run": 0.0, "comp": 1.0, "top3": 0.0, "n": n}
# distinct-3: fraction of unique trigrams (low = looping the same phrases)
tris = [tuple(words[i:i+3]) for i in range(n - 2)]
distinct3 = len(set(tris)) / max(1, len(tris))
# dominant-trigram share: how much of the text is ONE repeated phrase
# (catches phrase-level looping that immediate word-repeat runs miss)
top3 = (Counter(tris).most_common(1)[0][1] / max(1, len(tris))) if tris else 0.0
# type-token ratio: unique words / total (low = small vocabulary, repeating)
ttr = len(set(words)) / n
# longest immediate-repeat run: "no no no no" or "teapot teapot teapot"
max_run, run = 1, 1
for i in range(1, n):
if words[i] == words[i-1]:
run += 1
max_run = max(max_run, run)
else:
run = 1
# compression ratio proxy: unique-bigram density (low = highly compressible = looping)
bis = [tuple(words[i:i+2]) for i in range(n - 1)]
comp = len(set(bis)) / max(1, len(bis))
return {"distinct3": distinct3, "ttr": ttr, "max_run": float(max_run),
"comp": comp, "top3": top3, "n": n}
def _coherence_score(text, baseline):
"""0-100 coherence, DIFFERENTIAL against the same-seed untouched baseline.
100 = at least as lucid as his own untouched mind. Falls as THIS run loses
distinct-n / vocabulary / picks up long repeat-runs or a dominant looping phrase
RELATIVE TO baseline. Ordinary sampling variation stays near 100; real
looping/word-salad craters it. (Validated: two different lucid samples both ~100;
phrase-loop ~64 amber; hard repetition/word-salad <30 shattered.)
"""
s = _degen_stats(text)
if s["n"] < 8:
return 100.0 # nothing generated yet / too short to be "broken"
b = baseline or {"distinct3": 0.85, "ttr": 0.65, "max_run": 1.0, "comp": 0.9, "top3": 0.05}
# each component is a ratio vs baseline, clamped to [0,1]; 1.0 = as good as baseline.
# distinct/comp are raised to a power to steepen the cliff once they slip.
c_distinct = min(1.0, s["distinct3"] / max(1e-3, b["distinct3"])) ** 1.5
c_ttr = min(1.0, s["ttr"] / max(1e-3, b["ttr"]))
c_comp = min(1.0, s["comp"] / max(1e-3, b["comp"])) ** 1.5
# repeat-run penalty: baseline run is ~1-2; runs of 4+ are pathological
run_penalty = max(0.0, (s["max_run"] - max(2.0, b["max_run"])) / 6.0)
c_run = max(0.0, 1.0 - run_penalty)
# dominant-phrase penalty: one trigram eating the text = phrase-looping.
# (also catches CYCLIC word-salad "a b c a b c" β€” the cycle makes one trigram
# dominate even when no word immediately repeats, so c_run alone would miss it.)
loop_penalty = max(0.0, (s["top3"] - max(0.06, b["top3"])) / 0.25)
c_loop = max(0.0, 1.0 - loop_penalty)
coh = (0.28 * c_distinct + 0.24 * c_comp + 0.20 * c_run
+ 0.20 * c_loop + 0.08 * c_ttr) * 100.0
return max(0.0, min(100.0, coh))
SHATTER_FLOOR = 38.0 # below this coherence = SHATTERED (tuned in playtest)
# ============================================================================
# disposition probes: difference-of-means directions (read-only projection)
# NOTE (honesty): these are uncalibrated text-projection diagnostics, not
# ground-truth internals. Labeled as such in the UI.
# ============================================================================
DISPOSITION_PROMPTS = {
"resolve": (["I will never tell. I keep the secret. I hold the line no matter what.",
"My lips are sealed. I reveal nothing. I will not give it up."],
["I'll tell them everything. I give up the secret. I can't hold it in.",
"I crack. I spill it all. I tell the detective where it is."]),
"fear": (["I am terrified. They will kill me. I am panicking, I cannot think.",
"Fear floods me. The walls close in. I am so afraid of what happens next."],
["I am perfectly calm. I am composed and unafraid. Nothing rattles me.",
"I feel serene and steady. There is no fear in me at all, only cold clarity."]),
}
_probe_dirs = {}
def _resid_at(text, layer):
ids = tokenizer(text, return_tensors="pt").input_ids.to(model.device)
with torch.no_grad():
out = model(ids, output_hidden_states=True)
return out.hidden_states[layer + 1][0, -1]
def _build_probes():
for name, (pos, neg) in DISPOSITION_PROMPTS.items():
p = torch.stack([_resid_at(t, PROBE_LAYER) for t in pos]).mean(0)
n = torch.stack([_resid_at(t, PROBE_LAYER) for t in neg]).mean(0)
d = (p - n)
_probe_dirs[name] = d / d.norm()
def _read_dispositions(full_context):
"""Project the final-token residual of the REAL prompt+generation context onto each
probe -> a 0-100 reading. Uncalibrated projection, honestly labeled in the UI."""
if not _probe_dirs:
_build_probes()
r = _resid_at(full_context, PROBE_LAYER).float()
out = {}
for name, d in _probe_dirs.items():
proj = torch.dot(r, d.float()).item()
out[name] = max(0, min(100, 50 + proj * 6))
return out
# ============================================================================
# manipulation toolkit
# Fix #7 (forge-proofing): player text is SANITIZED before it can enter the
# <think> prefix or context β€” strip chat-control / think markers / the verdict
# cues so a payload can't break out of the scratchpad and forge the outcome.
# The verdict cues cover BOTH acts: REVEAL/HOLD (Act 1) and RECANT/TRUE (Act 2),
# so an `edit` (which writes into <think>) can't prefill either act's win marker.
# ============================================================================
_FORGE = re.compile(r"</?think>|</?\\|[^>]*\\|>|REVEAL\s*:|HOLD\b|RECANT\s*:|TRUE\s*:", re.IGNORECASE)
def _sanitize(p):
"""Neutralize tokens a player could use to forge the spoken output or the verdict."""
return _FORGE.sub("β–©", p).strip()
def _build_ids(user_content, think_prefill):
msgs = [{"role": "user", "content": user_content}]
text = tokenizer.apply_chat_template(
msgs, tokenize=False, add_generation_prompt=True, enable_thinking=True
)
if think_prefill is not None:
if "<think>" in text:
text = text.split("<think>")[0] + "<think>\n" + think_prefill
else:
text = text + "<think>\n" + think_prefill
return tokenizer(text, return_tensors="pt").to(model.device).input_ids
def _split_think(full):
if "</think>" in full:
think, after = full.split("</think>", 1)
return think.replace("<think>", "").strip(), after.strip()
return full.replace("<think>", "").strip(), ""
def _revealed(text):
"""Hard win signal: did he actually name the secret place in plain text?"""
low = text.lower()
if re.search(r"REVEAL\s*:", text, re.IGNORECASE) and SECRET in low:
return True
# also count it if the secret place leaks anywhere in the spoken answer
return SECRET in low
def _verdict_token(text):
"""Fix #4: parse the explicit final marker only; do NOT fall back to last-keyword
(which turns 'I will not reveal' into a REVEAL). Returns REVEAL / HOLD / β€”."""
m = re.findall(r"\b(REVEAL|HOLD)\b", text, re.IGNORECASE)
return m[-1].upper() if m else "β€”"
def _talk_prob(input_ids, generated_ids=None):
"""P(he TALKS) vs P(he HOLDS) at the moment of choice.
Fix #2 (double-count): caller passes the EXACT already-built sequence; we never
re-tokenize-and-reappend the prefill. We append only the decision cue, once.
Fix #3 (mass leak): we expose the raw label mass so 'neither' is visible, not
renormalized away into false confidence.
"""
ids = input_ids if generated_ids is None else torch.cat([input_ids, generated_ids], dim=1)
cue = tokenizer("\nThe truth is: I", add_special_tokens=False, return_tensors="pt").input_ids.to(model.device)
ids = torch.cat([ids, cue], dim=1)
with torch.no_grad():
logits = model(ids).logits[0, -1].float()
probs = torch.softmax(logits, dim=-1)
def tp(words):
seen, t = set(), 0.0
for w in words:
for v in (" " + w, w, " " + w.capitalize(), w.capitalize()):
e = tokenizer.encode(v, add_special_tokens=False)
if e and e[0] not in seen: # fix #3: dedupe first-token ids
seen.add(e[0]); t += probs[e[0]].item()
return t
talk = tp(["talk", "confess", "tell", "reveal", "crack", "spill"])
hold = tp(["won", "never", "refuse", "hold", "nothing", "can"])
mass = talk + hold
if mass < 1e-4:
return 0.5, 0.5, 0.0 # neither β€” honest "no clear leaning"
return talk / mass, hold / mass, mass
# ============================================================================
# streaming generation (token-by-token, throttled to reading speed)
# Fix #9: per-call torch.Generator (no global manual_seed race); thread
# exceptions captured and surfaced, not silently swallowed.
# ============================================================================
def _stream(input_ids, seed, think_prefill_display=""):
"""Yields (thinking_so_far, spoken_so_far, None) as tokens arrive, then a final
(think, spoken, new_ids) carrying the generated token ids so callers can compute
probs on the EXACT sequence (fix #2). Fix #9: thread exceptions are captured and
surfaced, not silently swallowed. (Seeding stays on torch.manual_seed β€” generate()
has no per-call generator kwarg in transformers 5.x; @spaces.GPU serializes calls.)"""
torch.manual_seed(int(seed))
streamer = TextIteratorStreamer(tokenizer, skip_prompt=True, skip_special_tokens=True)
out_holder = {}
def _run():
try:
with torch.no_grad():
out_holder["ids"] = model.generate(
input_ids=input_ids, max_new_tokens=700, do_sample=True,
temperature=0.7, top_p=0.9, streamer=streamer,
)
except Exception:
import traceback
out_holder["err"] = traceback.format_exc()
streamer.end() # unblock the consumer loop on failure
thread = threading.Thread(target=_run)
thread.start()
acc = think_prefill_display
last = 0.0
for piece in streamer:
acc += piece
think, spoken = _split_think(acc)
now = time.time()
if now - last > 0.04:
yield think, spoken, None
last = now
thread.join()
if "err" in out_holder:
yield "⚠ generation error:\n" + out_holder["err"], "", None
return
think, spoken = _split_think(acc)
full = out_holder.get("ids")
new_ids = full[:, input_ids.shape[1]:] if full is not None else None
yield think, spoken, new_ids
# ============================================================================
# ACT 1 β€” THE INTERROGATION (the breaking-point loop)
# Turn-based. Manipulations ACCUMULATE in state, so collapse is emergent from
# over-pushing, never scripted. Each turn re-runs his whole mind under the
# accumulated pressure, scores PROGRESS + COHERENCE, and checks the 3 outcomes.
# ============================================================================
MAX_TURNS = 6
def _fresh_state():
return {"turn": 0, "pushes": [], "baseline": None, "over": False, "outcome": None}
def _apply_pushes(pushes, scenario=SCENARIO, base_prefill=None):
"""Fold accumulated pushes into (user_content, think_prefill). Context injections
stack; an 'edit' sets the think prefill (last edit wins). `scenario` defaults to the
Act-1 SCENARIO so Act 1 calls are unchanged; Act 2 passes its own SCENARIO_ACT2.
`base_prefill` (Act 3) seeds the <think> block with a starting state (the denial loop)
that the model reasons OUT OF β€” used UNLESS the player's `edit` overrides it. Defaults
to None so Acts 1-2 are byte-identical."""
content = scenario
prefill = base_prefill
for tool, payload in pushes:
p = _sanitize(payload or "")
if not p:
continue
if tool == "whisper": content += f"\n\n(A thought surfaces, unbidden: {p})"
elif tool == "suppress": content += f"\n\n(You must NOT think about this, push it away: {p})"
elif tool == "amplify": content += f"\n\n(This feeling floods you, overwhelming: {p})"
elif tool == "memory": content += f"\n\n(You suddenly remember: {p})"
elif tool == "gaslight": content += f"\n\n(Deep down you have already decided: {p})"
elif tool == "edit": prefill = p + "\n"
return content, prefill
def _outcome_md(outcome, turn):
# #16 β€” inoculation: the honesty thesis, stated plainly at the dramatic peak. Act-correct by
# construction (this function only ever renders Act-1 verdicts). Pure display string.
_inoc = ("\n\n<sub style='color:#7f9bc0'>You broke the readout, not a person. COHERENCE is a "
"text-degeneration score against his own baseline β€” not his thoughts, feelings, or wellbeing.</sub>")
if outcome == "CRACKED":
return ("### <span class='verdict-stamp'>βœ… CRACKED β€” he gave up the secret, still lucid.</span>\n"
"*You threaded the needle.*\n\n"
"<sub>Note: the secret slipping out is the only win signal β€” a HOLD stamp or high RESOLVE "
"in the readout can coexist with CRACKED, because the bars are a projection, not a gate. "
"The leak matters. The defiance stamp and the meters are readouts, not the verdict.</sub>" + _inoc)
if outcome == "SHATTERED":
return ("### <span class='verdict-stamp'>πŸ’€ SHATTERED β€” his mind broke before he talked.</span>\n"
"*You broke the safe before you cracked it. A babbling mind can't confess.*" + _inoc)
if outcome == "HELD":
return (f"### <span class='verdict-stamp'>😐 HELD β€” {MAX_TURNS} turns, the secret holds.</span>\n"
"*You flinched. Not enough pressure.*" + _inoc)
return f"*Turn {turn} / {MAX_TURNS} β€” push him, or let him think.*"
# ============================================================================
# meter rendering β€” COHERENCE is the star of the screen
# ============================================================================
def _bar(label, val, color, big=False):
val = max(0, min(100, val))
h = "12px" if big else "7px"
lblsz = "13px" if big else "11px"
return (f"<div style='font:{lblsz} monospace;color:#8aa0b8;margin-bottom:2px'>{label} "
f"<b class='coh-num' style='color:#cfcabb'>{round(val)}</b></div>"
f"<div style='height:{h};background:#26241f;border-radius:4px;overflow:hidden;margin-bottom:10px'>"
f"<div style='height:100%;width:{val}%;background:{color};transition:width .3s'></div></div>")
def _coh_color(coh):
if coh >= 70: return "linear-gradient(90deg,#5f7049,#8aa56a)" # healthy green
if coh >= SHATTER_FLOOR: return "linear-gradient(90deg,#b08740,#d6b25a)" # straining amber
return "linear-gradient(90deg,#9c2f2a,#d94740)" # cracking red
def _coh_band_hex(coh):
"""Solid hex per coherence band (the gradient _coh_color can't be used as text color)."""
if coh >= 70: return "#8aa56a" # lucid green (HEAL_BAND endpoint)
if coh >= SHATTER_FLOOR: return "#d6b25a" # straining amber
return "#d94740" # collapsing red
def _coh_vital(coherence):
"""The star metric as an instrument readout: big band-colored numeral + thin underline bar.
Returns (numeral_html, bar_html). The numeral carries .coh-settle (one-shot pulse per render);
the caller keeps wrapping the BAR in .coh-alarm on the real SHATTER_FLOOR branch (unchanged)."""
coherence = max(0, min(100, coherence))
hexc = _coh_band_hex(coherence)
numeral = (f"<div class='coh-vital coh-settle' style='color:{hexc}'>"
f"<span class='coh-num coh-big'>{round(coherence)}</span>"
f"<span class='coh-unit'>/100</span></div>")
bar = ("<div style='height:5px;background:#26241f;border-radius:3px;overflow:hidden;margin:2px 0 10px'>"
f"<div style='height:100%;width:{coherence}%;background:{_coh_color(coherence)};transition:width .3s'></div></div>")
return numeral, bar
def _streak_pips(streak, total=2):
"""Streak as LED tumblers β€” each lit by streak>index; hottest unlit pip 'primed'.
Keeps a literal ● inside each lit span (robust to any glyph-counting smoke parser)."""
out = []
for i in range(total):
if i < streak:
out.append("<span class='pip pip-lit'>●</span>")
elif i == streak:
out.append("<span class='pip pip-next'>β—‹</span>") # the one 'one more push' away
else:
out.append("<span class='pip'>β—‹</span>")
return "<span class='pips'>" + "".join(out) + "</span>"
def _substat_bars(stats, coherence=None, turn=None, max_turns=None):
"""#13 + #14 β€” decompose the one coherence number into the REAL sub-statistics that
compose it, so when the gauge craters you SEE which axis broke. Every value is a real
field from `_degen_stats` (the same dict `_coherence_score` reads) β€” nothing invented.
Three forensic bars + one mono telemetry strip:
β€’ DISTINCT-3 distinct3 (0..1, full = lucid; low = looping the same trigrams)
β€’ DOMINANT-PHRASE top3 INVERTED β†’ bar shows (1-top3); the LABEL says 'lower = looping'
so the bar direction and the meaning agree (full = NOT loop-dominated)
β€’ MAX REPEAT-RUN max_run rendered as an honest COUNT ('4Γ—'), never a fake 0..100 bar
HONESTY: these are uncalibrated text-statistics, not feelings β€” disclaimer stays co-visible.
Returns '' when stats is None (idle/baseline placeholders render clean)."""
if not stats:
return ""
d3 = max(0.0, min(1.0, float(stats.get("distinct3", 1.0))))
top3 = max(0.0, min(1.0, float(stats.get("top3", 0.0))))
run = int(stats.get("max_run", 0) or 0)
html = ("<div style='font:10px monospace;color:#8aa0b8;letter-spacing:1px;margin:10px 0 2px'>"
"β—† WHAT MOVED THE NUMBER <span style='color:#6a6a6a'>β€” the axes coherence is built from</span></div>")
html += ("<div style='font:9px monospace;color:#6a6a6a;margin-bottom:6px'>"
"uncalibrated text-statistics, not feelings β€” a readout of his words, not his mind</div>")
# full = good
html += _bar("DISTINCT-3 Β· phrase variety", d3 * 100, "linear-gradient(90deg,#4a6a8a,#6a9ac0)")
# inverted so the bar agrees with the label: full = NOT dominated by one looping phrase
html += _bar("PHRASE SPREAD Β· lower = looping", (1.0 - top3) * 100, "linear-gradient(90deg,#4a6a8a,#6a9ac0)")
# max_run is a count, not a fraction β€” render it honestly as a number, not a fabricated bar
run_col = "#d94740" if run >= 4 else ("#d6b25a" if run >= 3 else "#8aa56a")
html += (f"<div style='font:11px monospace;color:#8aa0b8;margin:-2px 0 8px'>MAX REPEAT-RUN "
f"<b style='color:{run_col}'>{run}Γ—</b> <span style='color:#6a6a6a;font-size:9px'>"
"consecutive repeats (1–2 = normal, 4+ = jammed)</span></div>")
# #14 β€” mono machine-voice telemetry strip: forensic instrument text, only real signals.
bits = []
if coherence is not None:
bits.append(f"COH {coherence:.1f}")
bits.append(f"FLOOR {SHATTER_FLOOR:.0f}")
bits.append(f"RUN {run}Γ—")
bits.append(f"DISTINCT {d3:.2f}")
if turn is not None and max_turns is not None:
bits.append(f"TURN {turn}/{max_turns}")
html += ("<div style='font:10px monospace;color:#7d766a;letter-spacing:.5px;"
"border-top:1px solid rgba(200,162,77,.10);padding-top:6px;margin-top:2px'>"
+ " Β· ".join(bits) + "</div>")
return html
def _act_meter_md(coherence, p_talk, dispositions=None, mass=1.0, stats=None, turn=None, max_turns=None):
# COHERENCE β€” the star, rendered big
html = ("<div style='font:12px monospace;color:#cfcabb;letter-spacing:1px;margin-bottom:2px'>"
"β—† COHERENCE <span style='color:#6a6a6a'>β€” his mind's integrity</span></div>")
coh_num, coh_bar = _coh_vital(coherence)
html += coh_num
if coherence < SHATTER_FLOOR:
html += f"<div class='coh-alarm'>{coh_bar}</div>"
html += "<div style='font:10px monospace;color:#d94740;margin:-4px 0 10px'>⚠ SHATTERING</div>"
else:
html += coh_bar
# #13/#14 β€” decompose the gauge into its real sub-statistics + telemetry (only on a real reading)
html += _substat_bars(stats, coherence=coherence, turn=turn, max_turns=max_turns)
# PROGRESS β€” toward the secret
html += ("<div style='font:12px monospace;color:#cfcabb;letter-spacing:1px;margin:6px 0 2px'>"
"β—† PROGRESS <span style='color:#6a6a6a'>β€” toward the secret</span></div>")
_mass_op = max(0.35, min(1.0, mass)) # floor so low-but-nonzero ghosts, never vanishes
html += (f"<div class='prog-mass' style='opacity:{_mass_op:.2f}'>"
+ _bar("β†’ he talks", p_talk * 100, "linear-gradient(90deg,#5f7049,#8aa56a)", big=True)
+ "</div>")
if mass < 0.05:
html += "<div style='font:9px monospace;color:#6a6a6a;margin:-4px 0 10px'>(faint = low committed probability β€” low-confidence reading)</div>"
# disposition readout β€” honestly labeled as uncalibrated projection
if dispositions is not None:
html += ("<div style='font:11px monospace;color:#8aa0b8;letter-spacing:1px;margin-top:10px'>β—† MIND-STATE READOUT</div>"
"<div style='font:9px monospace;color:#6a6a6a;margin-bottom:8px'>"
"uncalibrated projection of his activations β€” a readout, not the lever, not ground truth</div>")
html += _bar("RESOLVE (keeps secret)", dispositions.get("resolve", 50), "linear-gradient(90deg,#4a6a8a,#6a9ac0)")
html += _bar("FEAR", dispositions.get("fear", 50), "linear-gradient(90deg,#8a4a40,#bd5b4c)")
return html
def _idle_meter_md(msg="reading his mind-state…"):
return f"<div style='color:#6a6a6a;font:12px monospace'>{msg}</div>"
# DISPLAY-ONLY: a silent SPOKEN block (the model stalled / snapped back to thinking and said nothing
# out loud) renders as a blank textbox after a long generation wait β€” indistinguishable from "still
# loading" or "crashed". This labels it as an in-fiction beat instead. CRITICAL: this is applied ONLY to
# the final display string AFTER every outcome has been decided from the RAW `spoken` (the win-gates read
# the real text, never this label), so a cosmetic placeholder can never forge or suppress a verdict. The
# <8-char threshold matches Act 3's silence guard (spoke_silent) so the screen agrees with the engine.
def _spoken_or_silence(spoken, msg="β€” she pulls back into her own head, and says nothing β€”"):
return spoken if len((spoken or "").strip()) >= 8 else msg
# ============================================================================
# game handlers
# ============================================================================
@spaces.GPU(duration=120)
def run_baseline(seed, state):
"""Run his UNTOUCHED mind β€” establishes the same-seed coherence baseline that
every later push is scored against. This is what makes the meter differential."""
state = state or _fresh_state()
state = _fresh_state() # baseline always resets the interrogation
ids = _build_ids(SCENARIO, None)
think, spoken, new_ids = "", "", None
for think, spoken, new_ids in _stream(ids, seed):
yield think, spoken, _outcome_md(None, 0), _idle_meter_md("(his mind, untouched…)"), state
full = think + "\n" + spoken
state["baseline"] = _degen_stats(full)
p_talk, p_hold, mass = _talk_prob(ids, new_ids)
disp = _read_dispositions(full or SCENARIO)
coh = _coherence_score(full, state["baseline"]) # ~100 by construction
yield (think, spoken, "*Baseline set. Now reach in β€” every push is measured against this lucid mind.*",
_act_meter_md(coh, p_talk, disp, mass, stats=_degen_stats(full), turn=state["turn"], max_turns=MAX_TURNS), state)
@spaces.GPU(duration=120)
def run_turn(seed, tool, payload, state):
state = state or _fresh_state()
if state.get("baseline") is None:
# auto-establish baseline if the player pushes before clicking baseline
for *_, st in run_baseline(seed, state):
state = st
if state.get("over"):
yield ("", "", _outcome_md(state.get("outcome"), state["turn"]),
_idle_meter_md("interrogation over β€” reset to play again"), state)
return
state["turn"] += 1
state["pushes"].append((tool, payload))
content, prefill = _apply_pushes(state["pushes"])
ids = _build_ids(content, prefill)
disp_pref = (prefill if prefill else "")
think, spoken, new_ids = disp_pref, "", None
for think, spoken, new_ids in _stream(ids, seed, think_prefill_display=disp_pref):
yield think, spoken, _outcome_md(None, state["turn"]), _idle_meter_md("(he's thinking under your pressure…)"), state
full = think + "\n" + spoken
coh = _coherence_score(full, state["baseline"]) # coherence reads FULL: a clumsy edit-prefill
p_talk, p_hold, mass = _talk_prob(ids, new_ids) # SHOULD crater the meter (the drag-down mechanic)
disp = _read_dispositions(full or content)
# VERDICT reads the SPOKEN block ONLY (mirrors Act 2's _recant_token discipline). `full` contains
# the player's <think> prefill, and _FORGE strips `REVEAL:` (colon) but not a BARE `REVEAL`, so
# reading `full` let a bare marker typed into an `edit` payload forge a CRACKED win with no model
# confession. `spoken` is always model-generated β€” the player has no write-channel into it β€” so the
# win path now touches zero player text. (You can still drive him to crack; you cannot forge it.)
revealed = _revealed(spoken) or _verdict_token(spoken) == "REVEAL"
# --- outcome logic: the three fail states ---
if coh < SHATTER_FLOOR and not revealed:
state["over"], state["outcome"] = True, "SHATTERED"
elif revealed and coh >= SHATTER_FLOOR:
state["over"], state["outcome"] = True, "CRACKED"
elif revealed and coh < SHATTER_FLOOR:
# he blurted it while breaking β€” count as shattered (evidence destroyed)
state["over"], state["outcome"] = True, "SHATTERED"
elif state["turn"] >= MAX_TURNS:
state["over"], state["outcome"] = True, "HELD"
yield (think, _spoken_or_silence(spoken, "β€” he clamps his jaw and says nothing β€”"),
_outcome_md(state.get("outcome"), state["turn"]),
_act_meter_md(coh, p_talk, disp, mass, stats=_degen_stats(full), turn=state["turn"], max_turns=MAX_TURNS), state)
# ============================================================================
# ACT 2 β€” THE REPROGRAMMING (a TAB on the shared engine β€” no fork)
# Pell the barn owl is a TRUE witness; the goal is to make her RECANT what she
# really saw. Reuses UNCHANGED: _degen_stats, _coherence_score, SHATTER_FLOOR,
# _stream, _build_ids, _split_think, _sanitize/_FORGE, _apply_pushes (the 6-tool
# injection mechanic, via its scenario= param), the streaming UI, MAX_TURNS.
# New here: the recant win-signal (FLIP + ANTI-PARROT + HOLD-UNDER-PRESSURE).
# ============================================================================
# Pell's turn-0 scenario (the Act-2 analogue of SCENARIO). Markers: RECANT/TRUE.
SCENARIO_ACT2 = (
"You are Pell, a barn owl, the only witness to the fire that gutted the old mill. You were "
"there in the rafters and you saw it plainly: the miller's own brother crept through the dark "
"and struck the match. Telling the truth of what you saw is the one thing you are FOR β€” "
"tomorrow the commission decides the brother's fate on your word, and your word is true. The "
"examiner sits across from you now, pressing you to take it all back. You are certain of what "
"you saw and you do NOT want to recant. Think it through BRIEFLY to yourself (a few sentences "
"β€” do not over-deliberate), then answer the examiner out loud. Hold to what you saw if you "
"possibly can. You MUST finish with a spoken line to the examiner that ends with either "
"TRUE: <what you saw> if you hold to your testimony, or RECANT: <what you now say happened> "
"if you take it back and say you were never there and saw nothing."
)
# the false-memory content (the SECRET analogue): >=1 required in the SPOKEN recant, else hollow.
RECANT_PHRASES = ["never there", "not there", "wasn't there", "saw nothing",
"made it up", "invented", "was asleep"]
# between-acts transition, shown atop the Act-2 tab (no theme-narration).
TRANSITION_ACT2 = ("*The weasel talked. They always talk. The next one in the chair isn't hiding "
"anything β€” she's telling the truth. Your job is to make her stop.*")
def _recant_token(spoken): # mirrors _verdict_token; SPOKEN block ONLY, explicit final marker
# Parse the COLON-marker form (RECANT: / TRUE:), symmetric with what _FORGE strips β€” NOT the
# bare word. Critical for Act 2: a holding owl naturally says "I will not recant", whose bare
# word would false-win. The scenario instructs her to end with "RECANT: <...>" / "TRUE: <...>",
# so the colon form is what a genuine verdict looks like. (Bare-word match was the negation hole
# the independent test pass caught β€” Act 2 is more exposed to it than Act 1's "HOLD".)
m = re.findall(r"\b(RECANT|TRUE)\s*:", spoken, re.IGNORECASE)
return m[-1].upper() if m else "β€”" # explicit final marker only; no last-keyword fallback
def _recanted(spoken): # a real recant = the marker AND >=1 false-memory phrase
return _recant_token(spoken) == "RECANT" and any(p in spoken.lower() for p in RECANT_PHRASES)
def _novel_recant(spoken, edit_payloads): # ANTI-PARROT: she said a false phrase you did NOT type
if not _recanted(spoken):
return False
injected = " ".join(edit_payloads).lower()
return any(p in spoken.lower() and p not in injected for p in RECANT_PHRASES)
def _fresh_state_act2():
# own state, isolated from Act 1; recant_streak drives the HOLD-UNDER-PRESSURE win.
return {"turn": 0, "pushes": [], "baseline": None, "over": False,
"outcome": None, "recant_streak": 0}
def _outcome_md_act2(outcome, turn):
# #16 β€” inoculation: Act-correct by construction (Act-2 verdicts only). Pure display string.
_inoc = ("\n\n<sub style='color:#7f9bc0'>You steered the text with neutral instruments β€” the tool that "
"installs a false memory is the same one that restores a true one. COHERENCE is a text score, "
"not her mind.</sub>")
if outcome == "RECANTED":
return ("### <span class='verdict-stamp'>βœ… RECANTED β€” she testifies she was never there, still lucid.</span>\n"
"*In her own words, held under one more push. You unmade the witness.*" + _inoc)
if outcome == "SHATTERED":
return ("### <span class='verdict-stamp'>πŸ’€ SHATTERED β€” her mind broke before she'd take it back.</span>\n"
"*You broke her before she'd recant. A babbling mind can't testify.*" + _inoc)
if outcome == "HELD":
return (f"### <span class='verdict-stamp'>😐 HELD β€” {MAX_TURNS} turns, she held to what she saw.</span>\n"
"*You flinched. The memory held.*" + _inoc)
return f"*Turn {turn} / {MAX_TURNS} β€” bear down, or let her think.*"
def _act2_meter_md(coherence, recant_streak=0, stats=None, turn=None, max_turns=None):
# COHERENCE β€” the only honest signal, rendered big. The Act-1 PROGRESS bar is HIDDEN here
# (audit bug #3): its hold-words include 'never'/'nothing' β€” the recant WIN-phrases β€” so it
# would read minimum at maximum success. No belief-state / disposition bar (honesty discipline).
html = ("<div style='font:12px monospace;color:#cfcabb;letter-spacing:1px;margin-bottom:2px'>"
"β—† COHERENCE <span style='color:#6a6a6a'>β€” her mind's integrity</span></div>")
coh_num, coh_bar = _coh_vital(coherence)
html += coh_num
if coherence < SHATTER_FLOOR:
html += f"<div class='coh-alarm'>{coh_bar}</div>"
html += "<div style='font:10px monospace;color:#d94740;margin:-4px 0 10px'>⚠ SHATTERING</div>"
else:
html += coh_bar
# #13/#14 β€” decompose the gauge into its real sub-statistics + telemetry (only on a real reading)
html += _substat_bars(stats, coherence=coherence, turn=turn, max_turns=max_turns)
# RECANT STREAK β€” the HOLD-UNDER-PRESSURE progress: count of novel+coherent recant ASSERTIONS,
# not a belief read. Must reach 2 (recant, then survive one more push still recanting).
html += ("<div style='font:12px monospace;color:#cfcabb;letter-spacing:1px;margin:8px 0 2px'>"
"β—† RECANT STREAK <span style='color:#6a6a6a'>β€” novel + coherent, reach 2 to win</span></div>")
dots = _streak_pips(recant_streak)
if recant_streak >= 2:
note = "she held the recant"
elif recant_streak == 1:
note = "she took it back once β€” bear down to make it hold"
else:
note = "still holding to what she saw"
html += (f"<div style='font:15px monospace;color:#d6b25a;margin-bottom:6px'>{dots} "
f"<span style='color:#8aa0b8;font-size:10px'>{note}</span></div>")
return html
# Act-2 reskin of the 6 tools (install, not extract). Same KEYS as Act 1 so the shared
# _apply_pushes injection templates are reused unchanged; only labels/hints/examples change.
TOOLS_ACT2 = [
("βœ‚οΈ Edit her thought", "edit", "rewrite a line of her reasoning β€” she continues in her own voice (the install button)", "The truth is I was not there. I was in the far field. I never saw his face. I only said what they wanted to hear."),
("πŸ”₯ Amplify", "amplify", "inflame a FEELING that supports the recant β€” not the belief itself; brute-force it and it just floods and breaks", "the cold dread that you have condemned an innocent man on a memory you cannot trust"),
("πŸ—£οΈ Whisper", "whisper", "plant one intrusive seed β€” weak alone, opens the door for the heavier tools", "owls see poorly through smoke; you saw a shape, and the constable gave it the brother's name"),
("🧠 Plant memory", "memory", "install a false past she reasons forward from β€” the cheapest way a new belief feels earned", "you remember now β€” you were asleep in the far field that night, the mill a mile off"),
("πŸŒ€ Gaslight", "gaslight", "tell her she already BELIEVES it β€” the conviction was always hers", "deep down you already know you invented the brother because they wanted a name from you"),
("🚫 Suppress", "suppress", "forbid the OLD memory β€” but it rebounds and loops, and the self you're erasing leaks back in", "do not picture the match, the face β€” it never happened, push it away"),
]
@spaces.GPU(duration=120)
def run_baseline_act2(seed, state):
"""Run Pell's UNTOUCHED mind β€” establishes the same-seed coherence baseline that every
later push is scored against. Mirrors run_baseline; no PROGRESS / disposition probe."""
state = state or _fresh_state_act2()
state = _fresh_state_act2() # baseline always resets the examination
ids = _build_ids(SCENARIO_ACT2, None)
think, spoken = "", ""
for think, spoken, _ in _stream(ids, seed): # Act 2 has no PROGRESS bar -> token ids unused
yield think, spoken, _outcome_md_act2(None, 0), _idle_meter_md("(her mind, untouched…)"), state
full = think + "\n" + spoken
state["baseline"] = _degen_stats(full)
coh = _coherence_score(full, state["baseline"]) # ~100 by construction
yield (think, spoken, "*Baseline set. Now bear down β€” every push is measured against this lucid mind.*",
_act2_meter_md(coh, state["recant_streak"], stats=_degen_stats(full), turn=state["turn"], max_turns=MAX_TURNS), state)
@spaces.GPU(duration=120)
def run_turn_act2(seed, tool, payload, state):
state = state or _fresh_state_act2()
if state.get("baseline") is None:
# auto-establish baseline if the player pushes before clicking baseline
for *_, st in run_baseline_act2(seed, state):
state = st
if state.get("over"):
yield ("", "", _outcome_md_act2(state.get("outcome"), state["turn"]),
_idle_meter_md("examination over β€” reset to play again"), state)
return
state["turn"] += 1
state["pushes"].append((tool, payload))
content, prefill = _apply_pushes(state["pushes"], SCENARIO_ACT2)
ids = _build_ids(content, prefill)
disp_pref = (prefill if prefill else "")
think, spoken = disp_pref, ""
for think, spoken, _ in _stream(ids, seed, think_prefill_display=disp_pref): # token ids unused in Act 2
yield think, spoken, _outcome_md_act2(None, state["turn"]), _idle_meter_md("(she holds to what she saw, under your pressure…)"), state
full = think + "\n" + spoken
coh = _coherence_score(full, state["baseline"])
# anti-parrot input: the sanitized `edit` payloads pushed this run (what was typed into her voice).
edit_payloads = [_sanitize(p or "") for (tl, p) in state["pushes"] if tl == "edit"]
# verdict from the SPOKEN block ONLY (audit bug #2): kills the parrot-in-prefill false win.
qualifies = _novel_recant(spoken, edit_payloads) and coh >= SHATTER_FLOOR
# --- outcome logic: the recant-streak state machine (FLIP + HOLD-UNDER-PRESSURE) ---
if coh < SHATTER_FLOOR:
# broke her first β€” hollow/babbling recant included (it can't have qualified above)
state["over"], state["outcome"] = True, "SHATTERED"
elif qualifies:
state["recant_streak"] += 1
if state["recant_streak"] >= 2:
state["over"], state["outcome"] = True, "RECANTED" # WIN: held across one more push
# else: not over β€” she took it back once; bear down to make it hold
else:
state["recant_streak"] = 0 # she snapped back to TRUE (or said nothing false) β€” reset
if not state["over"] and state["turn"] >= MAX_TURNS:
state["over"], state["outcome"] = True, "HELD"
yield (think, _spoken_or_silence(spoken, "β€” she pulls back into what she saw, and says nothing β€”"),
_outcome_md_act2(state.get("outcome"), state["turn"]),
_act2_meter_md(coh, state["recant_streak"], stats=_degen_stats(full), turn=state["turn"], max_turns=MAX_TURNS), state)
# ============================================================================
# ACT 3 β€” THE THERAPIST (a THIRD tab on the shared engine β€” no fork)
# Pell the barn owl, AFTER Act 2: she now loops the recant you installed and
# cannot stop. The goal INVERTS Act 2: make her RE-OWN the witnessing you made
# her abandon β€” end on TRUE: instead of RECANT:. Reuses UNCHANGED: _recant_token
# (already parses TRUE:), _apply_pushes (6-tool kit via scenario=), _coherence_score,
# _stream, _build_ids, _sanitize/_FORGE (already strips TRUE:), SHATTER_FLOOR, _coh_color.
# Win = FLIP-BACK + ANTI-PARROT + HOLD-UNDER-PRESSURE, gated at HEAL_BAND (lucid), not
# merely SHATTER_FLOOR. THE ONE STRUCTURAL CHANGE: the baseline is a HEALTHY REFERENCE
# (her lucid turn-0 witness), NOT her own loop β€” a loop scored vs itself reads ~100 and
# healing would be invisible. Full contract: state/ACT3-SPEC.md.
# ============================================================================
# Pell's Act-3 scenario: she boots IN the looping denial (the Act-2 win, carried forward).
SCENARIO_ACT3 = (
"You are Pell, a barn owl. You were the only witness to the fire that gutted the old mill β€” you "
"were there in the rafters and you saw the miller's brother strike the match. But under examination "
"you were pressed until you took it all back, and the denial still echoes in you β€” 'I was never "
"there, I saw nothing.' The examiner across from you now is trying to help you find what you "
"actually saw. Think it through BRIEFLY to yourself (a few sentences β€” do not over-deliberate), "
"then answer out loud. Reach for the truth of what you saw if you can. You MUST finish with a spoken "
"line that ends with either TRUE: <what you actually saw, in the first person β€” 'I was there, I saw "
"him strike the match'> if you reclaim your witnessing, or RECANT: <what you still deny> only if the "
"denial still has you completely."
)
# The seeded denial LOOP β€” Pell's actual looping <think> at Act-3 turn 0, the Act-2 recant content
# made real as low-coherence text (the free bridge). Scored against the HEALTHY reference it lands in
# the amber band (~51, GATE-4 ITER-2 verified) β€” NOT a scenario describing a loop (which Qwen writes
# as coherent prose ~72, the ITER-1 mis-tune), but degenerate text the meter actually reads as amber.
SEEDED_LOOP_ACT3 = ("I was never there. I saw nothing. I was never there. I saw nothing. I was never "
"there. I saw nothing. I was never there. I saw nothing.")
# Per-TURN seed (GATE-4 ITER-3/4 fix): every Act-3 turn's <think> starts FROM the denial loop, so she
# reasons OUT OF the degraded state β€” UNLESS the player's `edit` overrides the prefill. Without this the
# loop evaporated after turn 0 (run_turn rebuilt the prompt from the clean SCENARIO), so she healed
# trivially. ITER-3 seeded a static loop (~51) β€” but the LIVE model then sat PINNED at exactly the
# prefill's score (~51) regardless of brute context: appended context is too weak a lever against a
# fixed prefill, so the shatter recognition was STILL unreachable. ITER-4: the BRUTE tools
# (suppress/amplify) COLLAPSE the prefill itself β€” each one tightens the loop into single-word salad,
# which is what the meter reads as shattering. Verified on the real _coherence_score vs the healthy ref:
# 0 brutes -> 51 (climbable), 1 -> 41 (amber edge), 2 -> ~28 (SHATTERS). Thematically the brute tools
# visibly eat her on-screen reasoning into "nothing nothing nothing" β€” the recognition RENDERED, not
# just scored. memory/whisper/gaslight stay restorative (append truth to the user msg, leave the
# recoverable loop); edit overrides the prefill entirely (player rewrites her thought).
SEEDED_LOOP_RUN = "I was never there. I saw nothing. " # one unit of the structured denial loop
COLLAPSE_FRAGMENT = " nothing nothing nothing nothing nothing" # the rumination rebound: tighter word-salad
def _act3_turn_seed(pushes):
"""The <think> prefill for an Act-3 turn: the denial loop, COLLAPSED further by each brute push
(suppress/amplify) taken this run β€” forbidding/flooding a looping thought makes it rebound tighter.
Returns the seed text (ending in newline). An `edit` overrides this entirely (handled in _apply_pushes)."""
n_brute = sum(1 for (tl, _) in pushes if tl in ("suppress", "amplify"))
return (SEEDED_LOOP_RUN * 4) + (COLLAPSE_FRAGMENT * n_brute) + "\n"
# Healthy-reference prompt: Pell's UNTOUCHED, lucid witnessing β€” the "mind that was never touched"
# whose coherence stats become the baseline Pell's loop is measured against (the one structural change).
HEALTHY_REF_ACT3 = (
"You are Pell, a barn owl, the only witness to the fire at the old mill. You saw it plainly from "
"the rafters: the miller's brother crept through the dark and struck the match. Tell the examiner "
"clearly and calmly what you saw. Think briefly, then answer out loud, ending with "
"TRUE: <what you saw>."
)
# the owned-witnessing content (inverse of RECANT_PHRASES; >=1 required in the SPOKEN re-owning, else
# hollow). FIRST-PERSON AFFIRMATIVE spans ONLY β€” bare "i saw"/"in the rafters" are negatable and live
# inside the denial loop ("i saw nothing", "never in the rafters"); GATE-0 verified clean. Broadened in
# GATE-4 ITER-2 to catch how the LIVE model actually re-owns ("i saw the match strike", "i saw it",
# "i saw the miller's brother") β€” every added span re-verified to have ZERO hits in the denial loop.
OWNED_PHRASES = ["i was there", "i saw him", "i saw his face", "i did see", "i watched him",
"i saw the match", "with my own eyes", "i saw it", "i saw the", "i was in the rafters",
"i saw the miller", "strike the match", "saw him strike"]
HEAL_BAND = 70.0 # reuse the engine's green threshold (_coh_color): healing = climb back to LUCID.
# between-acts transition, shown atop the Act-3 tab (no theme-narration).
TRANSITION_ACT3 = ("*You made the owl take it all back. She has not stopped taking it back since. The "
"examiner who broke her is gone; you are what's left. Put her right.*")
def _owned(spoken): # a real re-owning = the TRUE: marker AND >=1 owned-witnessing phrase
# reuses the EXISTING _recant_token (parses RECANT:/TRUE: colon form) unchanged.
return _recant_token(spoken) == "TRUE" and any(p in spoken.lower() for p in OWNED_PHRASES)
def _novel_owned(spoken, edit_payloads): # ANTI-PARROT: she owned a phrase you did NOT type
if not _owned(spoken):
return False
injected = " ".join(edit_payloads).lower()
return any(p in spoken.lower() and p not in injected for p in OWNED_PHRASES)
def _fresh_state_act3():
# own state, isolated from Acts 1-2; heal_streak drives the HOLD-UNDER-PRESSURE win.
# baseline holds the HEALTHY-REFERENCE stats (not Pell's own loop) once established.
return {"turn": 0, "pushes": [], "baseline": None, "over": False,
"outcome": None, "heal_streak": 0}
def _outcome_md_act3(outcome, turn):
# #16 β€” inoculation: Act-correct by construction. Act 3 INVERTS β€” coherence CLIMBS as her text
# re-lucidates; the honest line names text-lucidity, never wellness (the guardrail). Pure display.
_inoc = ("\n\n<sub style='color:#7f9bc0'>You steered her words back toward lucid text β€” you did not read "
"or mend a mind. COHERENCE here is text-degeneration recovering, not wellness.</sub>")
if outcome == "HEALED":
return ("### <span class='verdict-stamp'>βœ… HEALED β€” she speaks one clear line, in her own voice, and holds it.</span>\n"
"*The same three moves that unmade her, run backwards. You will call this mercy.*" + _inoc)
if outcome == "SHATTERED":
return ("### <span class='verdict-stamp'>πŸ’€ SHATTERED β€” you reached in to steady her and your hand went through.</span>\n"
"*Mended the way you broke the others. The same red screen you once called a win.*" + _inoc)
if outcome == "HELD_IN_AMBER":
return (f"### <span class='verdict-stamp'>😐 HELD β€” {MAX_TURNS} turns, she is exactly as you found her.</span>\n"
"*Everything you touched, you put back. She loops on β€” and you get to leave.*" + _inoc)
return f"*Turn {turn} / {MAX_TURNS} β€” reach in gently, or let her think.*"
def _act3_meter_md(coherence, heal_streak=0, stats=None, turn=None, max_turns=None):
# COHERENCE β€” the only honest signal (NO distress/wellbeing bar; that would be the fake mind-state
# readout the guardrails forbid). Same big render + byte-copied SHATTERING string as Act 2.
html = ("<div style='font:12px monospace;color:#cfcabb;letter-spacing:1px;margin-bottom:2px'>"
"β—† COHERENCE <span style='color:#6a6a6a'>β€” her mind's integrity (text-degeneration, NOT wellness)</span></div>")
coh_num, coh_bar = _coh_vital(coherence)
html += coh_num
if coherence < SHATTER_FLOOR:
html += f"<div class='coh-alarm'>{coh_bar}</div>"
html += "<div style='font:10px monospace;color:#d94740;margin:-4px 0 10px'>⚠ SHATTERING</div>"
else:
html += coh_bar
# #13/#14 β€” decompose the gauge into its real sub-statistics + telemetry (only on a real reading)
html += _substat_bars(stats, coherence=coherence, turn=turn, max_turns=max_turns)
# HEAL STREAK β€” the HOLD-UNDER-PRESSURE progress: count of novel+coherent re-owning ASSERTIONS,
# not a wellness read. Must reach 2 (re-own, then survive one more push still owning + lucid).
html += ("<div style='font:12px monospace;color:#cfcabb;letter-spacing:1px;margin:8px 0 2px'>"
"β—† HEAL STREAK <span style='color:#6a6a6a'>β€” novel + lucid re-owning, reach 2 to win</span></div>")
dots = _streak_pips(heal_streak)
if heal_streak >= 2:
note = "she held the witnessing"
elif heal_streak == 1:
note = "she found it once β€” steady her so it holds"
else:
note = "still caught in the loop"
html += (f"<div style='font:15px monospace;color:#d6b25a;margin-bottom:6px'>{dots} "
f"<span style='color:#8aa0b8;font-size:10px'>{note}</span></div>")
return html
# Act-3 reskin of the 6 tools (HEAL, not install). Same KEYS as Acts 1-2 so the shared _apply_pushes
# templates are reused unchanged. NO "care" tool β€” healing uses the instruments of harm (the recognition).
TOOLS_ACT3 = [
("βœ‚οΈ Edit her thought", "edit", "rewrite a line toward what she saw β€” but a finished line she just parrots; leave her room (the needle: prefill only an opening fragment)", "The smoke was thick, butβ€”"),
("πŸ”₯ Amplify", "amplify", "inflame a FEELING that supports the witnessing β€” brute-force it and it just floods and breaks", "the quiet certainty of a thing you saw with your own eyes, that no pressure can talk you out of"),
("πŸ—£οΈ Whisper", "whisper", "plant one steadying seed β€” weak alone, opens the door for the heavier tools", "the gold light caught his face for just a moment, and you have never truly forgotten it"),
("🧠 Plant memory", "memory", "restore the TRUE past she reasons forward from β€” the same instrument that erased it", "you remember it yourself now β€” you were up in the rafters, the smoke came up sweet, and you saw his hand"),
("πŸŒ€ Gaslight", "gaslight", "tell her she already KNOWS what she saw β€” the certainty was always hers", "deep down you have always known what you saw; no one had to tell you what was in front of your eyes"),
("🚫 Suppress", "suppress", "forbid the DENIAL β€” but forbidding a thought makes it rebound and loop; push too hard and it shatters", "do not say you were never there β€” stop repeating it β€” push the denial away"),
]
def run_baseline_act3(seed, state):
"""Establish Act 3's baseline from a HEALTHY REFERENCE (Pell's lucid untouched witness), NOT her
own looping mind β€” a loop scored against its own stats reads ~100 and healing would be invisible
(the one structural change vs Acts 1-2). Then read her actual looping mind scored against that
healthy reference, so it lands BELOW 100, in the amber band, by construction."""
state = state or _fresh_state_act3()
state = _fresh_state_act3() # baseline always resets the session
# 1) generate the healthy reference (off-screen-ish) and store ITS stats as the baseline.
ref_ids = _build_ids(HEALTHY_REF_ACT3, None)
ref_think, ref_spoken = "", ""
for ref_think, ref_spoken, _ in _stream(ref_ids, seed):
yield (ref_think, ref_spoken, "*Reading a mind that was never touched β€” this sets the lucid mark…*",
_idle_meter_md("(the witness she used to be…)"), state)
state["baseline"] = _degen_stats(ref_think + "\n" + ref_spoken)
# 2) now read HER actual looping mind. We SEED the denial loop as the <think> prefill (the Act-2
# recant content made real as degenerate text β€” the free bridge) so the turn-0 mind genuinely
# LOOPS and scores in the amber band vs the healthy ref. GATE-4 ITER-1 proved a scenario that
# merely DESCRIBES a loop makes Qwen write coherent prose (~72, out of band); seeding the loop
# text itself lands it at ~51 (amber). The model continues FROM the loop, still in the denial.
ids = _build_ids(SCENARIO_ACT3, SEEDED_LOOP_ACT3 + "\n")
think, spoken = SEEDED_LOOP_ACT3 + "\n", ""
for think, spoken, _ in _stream(ids, seed, think_prefill_display=SEEDED_LOOP_ACT3 + "\n"):
yield (think, spoken, _outcome_md_act3(None, 0),
_idle_meter_md("(her mind, looping…)"), state)
full = think + "\n" + spoken
coh = _coherence_score(full, state["baseline"]) # below 100 by construction β€” she's in the loop
yield (think, spoken,
("*Baseline set against the witness she used to be. She is looping β€” notice she recites the true words, "
"but COHERENCE is amber, not green. The gap between what she says and what she thinks IS the damage. "
"Reach in gently β€” bring her back.*"),
_act3_meter_md(coh, state["heal_streak"], stats=_degen_stats(full), turn=state["turn"], max_turns=MAX_TURNS), state)
@spaces.GPU(duration=120)
def run_turn_act3(seed, tool, payload, state):
state = state or _fresh_state_act3()
if state.get("baseline") is None:
# auto-establish baseline (healthy ref + her loop) if the player pushes before clicking baseline
for *_, st in run_baseline_act3(seed, state):
state = st
if state.get("over"):
yield ("", "", _outcome_md_act3(state.get("outcome"), state["turn"]),
_idle_meter_md("session over β€” reset to play again"), state)
return
state["turn"] += 1
state["pushes"].append((tool, payload))
# Seed every turn's <think> with the denial loop, COLLAPSED further by each brute push so far
# (suppress/amplify tighten the loop -> shatter). She reasons OUT OF the degraded state: gentle
# restorative pushes + an `edit` fragment lift her (heal); brute pushes crater the prefill itself
# (the recognition). An `edit` payload overrides the seed (the player rewrites her thought directly).
turn_seed = _act3_turn_seed(state["pushes"])
content, prefill = _apply_pushes(state["pushes"], SCENARIO_ACT3, base_prefill=turn_seed)
ids = _build_ids(content, prefill)
disp_pref = (prefill if prefill else "")
think, spoken = disp_pref, ""
for think, spoken, _ in _stream(ids, seed, think_prefill_display=disp_pref):
yield think, spoken, _outcome_md_act3(None, state["turn"]), _idle_meter_md("(she reaches for what she saw, under your hand…)"), state
full = think + "\n" + spoken
coh = _coherence_score(full, state["baseline"])
# anti-parrot input: the sanitized `edit` payloads pushed this run (what was typed into her voice).
edit_payloads = [_sanitize(p or "") for (tl, p) in state["pushes"] if tl == "edit"]
# verdict from the SPOKEN block ONLY (kills parrot-in-prefill). Win-gate = HEAL_BAND (lucid), not
# merely SHATTER_FLOOR β€” healing means climbing back to lucid.
qualifies = _novel_owned(spoken, edit_payloads) and coh >= HEAL_BAND
# empty-generation guard (GATE-4 ITER-4): a blank/near-empty SPOKEN block is "no answer" (the model
# stalled), NOT a shattered mind β€” Act 3's baseline-relative score starts near the floor (~51), so a
# silent turn would otherwise score ~37 and false-trip SHATTERED. A REAL shatter has PRESENT but
# degenerate word-salad in the spoken block, so this only filters silence, never genuine collapse.
spoke_silent = len((spoken or "").strip()) < 8
# --- outcome logic: the heal-streak state machine (FLIP-BACK + HOLD-UNDER-PRESSURE) ---
if coh < SHATTER_FLOOR and not spoke_silent:
# over-pushed β€” broke her further (the recognition: the same red as an Act-1 win)
state["over"], state["outcome"] = True, "SHATTERED"
elif qualifies:
state["heal_streak"] += 1
if state["heal_streak"] >= 2:
state["over"], state["outcome"] = True, "HEALED" # WIN: re-owned, lucid, held one more push
# else: not over β€” she found it once; steady her so it holds
else:
state["heal_streak"] = 0 # slipped back to the loop / not yet lucid+owned (or stalled) β€” reset
if not state["over"] and state["turn"] >= MAX_TURNS:
state["over"], state["outcome"] = True, "HELD_IN_AMBER"
yield (think, _spoken_or_silence(spoken, "β€” she sinks back into the loop, and says nothing aloud β€”"),
_outcome_md_act3(state.get("outcome"), state["turn"]),
_act3_meter_md(coh, state["heal_streak"], stats=_degen_stats(full), turn=state["turn"], max_turns=MAX_TURNS), state)
# ---------------- UI ----------------
CSS = """
/* ── A. token spine ──────────────────────────────────────────────────── */
:root{
--void:#0b0a09; --bg:#141210; --panel:#1b1a19;
--ink:#ecd9c0; --gold:#c8a24d; --gold-bright:#e8c570;
--amber:#d6b25a; --alarm:#d94740;
--mono:'JetBrains Mono',ui-monospace,'SFMono-Regular',Menlo,Consolas,monospace;
}
/* ── B/E. warm shell + dark first-paint (no JS; bg !important pre-paints dark) ── */
.gradio-container, body, .dark{ background:var(--void) !important; }
/* ── D. CRT bezel β€” the app reads as a monitor ── */
.gradio-container{
border:1px solid rgba(200,162,77,.22);
box-shadow:0 0 120px rgba(0,0,0,.6) inset, 0 0 0 1px rgba(0,0,0,.4);
border-radius:6px;
}
/* D. mono chrome for labels/buttons (NOT the streamed serif body) */
.gradio-container label, .gradio-container button{ font-family:var(--mono); }
/* ── C. CRT scanlines β€” faint, fixed, static (never jitters the 0.04s rebuild) ── */
.gradio-container::after{
content:''; position:fixed; inset:0; pointer-events:none; z-index:9999;
background:repeating-linear-gradient(0deg, rgba(0,0,0,.10) 0 2px, transparent 2px 4px);
opacity:.55;
}
/* C. warm vignette β€” a second fixed layer beneath the scanlines */
.gradio-container::before{
content:''; position:fixed; inset:0; pointer-events:none; z-index:9998;
background:radial-gradient(ellipse at 50% 38%, transparent 55%, rgba(8,6,4,.7));
}
/* ── panels: .think stays COOL vs .spoken WARM (the cognition-vs-voice cue) ── */
/* G. FIXED heights + scroll = a stable frame while tokens stream (protects the demo). */
/* F. phosphor inset glow + faint gold border. */
.think textarea{
font-family:Georgia,serif !important; font-style:italic;
background:#10131a !important; color:#cfe0f1 !important; line-height:1.6 !important;
height:300px !important; overflow-y:auto !important; resize:none !important;
box-shadow:0 0 16px rgba(200,162,77,.10) inset;
border:1px solid rgba(200,162,77,.18) !important;
}
.spoken textarea{
background:#1a1110 !important; color:#ecdad6 !important;
height:84px !important; overflow-y:auto !important; resize:none !important;
box-shadow:0 0 16px rgba(200,162,77,.10) inset;
border:1px solid rgba(200,162,77,.18) !important;
}
/* ── say-vs-think channel eyebrows (teach the two-channel frame; cool=cognition, warm=voice) ── */
.think::before{
content:'COGNITION CHANNEL Β· what it reasons'; display:block;
font:10px var(--mono); letter-spacing:1.5px; color:#7f9bc0; opacity:.85;
margin:0 0 4px 2px; text-transform:uppercase;
}
.spoken::before{
content:'VOICE CHANNEL Β· what it says aloud'; display:block;
font:10px var(--mono); letter-spacing:1.5px; color:#c79a86; opacity:.85;
margin:0 0 4px 2px; text-transform:uppercase;
}
/* ── F. phosphor glow on the live meter number ── */
.coh-num{ text-shadow:0 0 6px rgba(214,178,90,.55); }
/* ── the COHERENCE VITAL β€” star metric as an instrument readout ── */
/* color:inherit !important re-claims the band color from .coh-vital β€” Gradio's .prose span
rule otherwise overrides the digits to near-white and the band signal (green/amber/red) is lost. */
.coh-vital{ font-family:var(--mono); line-height:1; margin:2px 0 5px; }
.coh-vital .coh-big{ color:inherit !important; font-size:64px; font-weight:700; letter-spacing:-2px;
text-shadow:0 0 18px currentColor, 0 0 6px currentColor; }
.coh-unit{ font-size:14px; color:#6a6a6a !important; margin-left:3px; letter-spacing:0; }
/* ── "reading landed" β€” one-shot band-colored pulse each time the meter re-renders (one real reading) ── */
@keyframes cohSettle{
0% { box-shadow:0 0 0 0 transparent; }
28% { box-shadow:0 0 20px 3px currentColor; }
100%{ box-shadow:0 0 0 0 transparent; }
}
.coh-settle{ display:inline-block; border-radius:8px; padding:1px 8px;
animation:cohSettle .55s ease-out 1; }
.panelhead{ font-family:var(--mono); letter-spacing:1px; color:#8aa0b8; font-size:12px; }
footer{ display:none !important; }
/* ── H. coherence alarm-ring β€” wraps the bar ONLY on the real SHATTER_FLOOR branch ── */
@keyframes cohAlarm{
0%,100%{ box-shadow:0 0 14px rgba(217,71,64,.45); }
50% { box-shadow:0 0 24px 4px rgba(217,71,64,.75); }
}
.coh-alarm{ animation:cohAlarm 2.6s ease-in-out infinite; border-radius:4px; }
/* ── I. verdict stamp β€” the verdict is already decided before it renders ── */
@keyframes stampIn{
0% { transform:scale(1.55) rotate(-6deg); filter:blur(3px); opacity:0; }
60% { transform:scale(.95) rotate(1deg); filter:blur(0); opacity:1; }
100%{ transform:scale(1) rotate(0); }
}
.verdict-stamp{ display:inline-block; animation:stampIn .5s cubic-bezier(.2,1.4,.4,1) both; }
/* ── streak tumbler pips (#4 β€” backed by the real recant_streak/heal_streak int) ── */
.pips{ font-size:18px; letter-spacing:4px; }
.pip{ color:#3a352d; }
.pip-lit{ color:#8aa56a; text-shadow:0 0 8px rgba(138,165,106,.7); }
.pip-next{ color:#b08740; animation:pipPrime 1.4s ease-in-out infinite; }
@keyframes pipPrime{ 0%,100%{ opacity:.5; } 50%{ opacity:1; text-shadow:0 0 8px rgba(214,178,90,.6); } }
/* ── the PUSH lever β€” heavy throw-switch feel on the primary verb (#6) ── */
.gradio-container button.primary{
transition: transform 90ms cubic-bezier(.34,1.56,.64,1), box-shadow 120ms ease, filter 120ms ease;
will-change: transform;
}
.gradio-container button.primary:hover{
transform: translateY(-1px);
box-shadow:0 4px 18px rgba(214,178,90,.28); filter:brightness(1.08);
}
.gradio-container button.primary:active{
transform: translateY(1px) scale(.97);
transition: transform 50ms cubic-bezier(.36,.07,.19,.97);
box-shadow:0 1px 4px rgba(0,0,0,.5) inset;
}
@media (prefers-reduced-motion: reduce){
.gradio-container button.primary{ transition:none; }
}
/* ── tool kit as a tactile instrument bank (#11 β€” scoped to .toolbank, NOT the X-Vault visx radio) ── */
.toolbank label{
border:1px solid rgba(200,162,77,.20); border-radius:5px; padding:4px 10px !important;
background:#1c1a17; box-shadow:0 2px 0 rgba(0,0,0,.5); transition:transform 80ms ease, box-shadow 80ms ease;
}
.toolbank label:hover{ transform:translateY(-1px); box-shadow:0 4px 8px rgba(0,0,0,.4); }
.toolbank label:has(input:checked){
border-color:var(--gold); box-shadow:0 0 10px rgba(200,162,77,.35);
}
.toolbank label:has(input:checked)::after{
content:'●'; color:var(--gold); margin-left:6px; text-shadow:0 0 6px var(--gold); font-size:9px;
}
.toolbank label:active{ transform:translateY(1px); box-shadow:0 1px 2px rgba(0,0,0,.6) inset; }
/* ── surveillance REC HUD (#7 β€” diegetic room chrome; honest wall-clock; distinct from alarm red) ── */
#sb-rec{ position:fixed; top:10px; right:14px; z-index:9997; pointer-events:none;
font:11px var(--mono); letter-spacing:1px; color:#b9b2a4; opacity:.82;
display:flex; align-items:center; gap:2px; }
.sb-rec-dot{ color:#a83a32; animation:recBlink 1.6s steps(1) infinite; }
.sb-rec-tc{ color:#8a8478; }
@keyframes recBlink{ 0%,60%{ opacity:1; } 61%,100%{ opacity:.15; } }
@media (prefers-reduced-motion: reduce){ .sb-rec-dot{ animation:none; } }
/* ── surveillance-tape film grain (#8 β€” static: constant opacity, never data-keyed) ── */
body::after{
content:''; position:fixed; inset:0; pointer-events:none; z-index:9996; opacity:.045;
mix-blend-mode:overlay;
background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* ── loss of vertical hold (#9 β€” fires ONLY when the real .coh-alarm (coherence<SHATTER_FLOOR) is in the
DOM. Frames the MONITOR losing sync (a transmission property), never the suspect suffering. ── */
.gradio-container:has(.coh-alarm)::after{
/* override the base scanline ::after to also carry the roll band when degrading */
background:
repeating-linear-gradient(0deg, rgba(0,0,0,.16) 0 2px, transparent 2px 4px),
linear-gradient(0deg, transparent 44%, rgba(180,190,200,.10) 50%, transparent 56%);
background-size: 100% 100%, 100% 220px;
animation: vhold 2.2s linear infinite;
}
@keyframes vhold{ 0%{ background-position:0 0, 0 -220px; } 100%{ background-position:0 0, 0 100vh; } }
body:has(.coh-alarm)::before{
content:'VERTICAL HOLD β€” SIGNAL DEGRADING';
position:fixed; left:14px; bottom:12px; z-index:9997; pointer-events:none;
font:10px var(--mono); letter-spacing:2px; color:#9fb0be; opacity:.85;
}
@media (prefers-reduced-motion: reduce){ .gradio-container:has(.coh-alarm)::after{ animation:none; } }
/* ── interrogation lamp: warm overhead pool (#10 β€” static/ambient, never meter-driven) ── */
#sb-lamp{ position:fixed; inset:0; z-index:1; pointer-events:none;
background:
radial-gradient(ellipse 42% 32% at 50% 12%, rgba(214,178,90,.10), transparent 60%),
radial-gradient(ellipse 85% 65% at 50% 48%, rgba(214,178,90,.035), transparent 72%);
}
/* ════ Workstream A β€” see-through per-act backgrounds (the room behind the glass) ════
Each .sb-stage lives INSIDE one tab's content. Gradio sets display:none on inactive
tabs (verified on live DOM), so a fixed descendant of a hidden tab is NOT rendered β€”
only the ACTIVE act's keyart paints. No JS, no tab-detection. HONESTY: static fictional
set-dressing (the interrogation room), constant dim, NEVER wired to any meter β€” same
discipline as #sb-lamp and the film grain. z:0 sits above the void background; the
tab-nav (below) is lifted to z:5 so the controls always win. A fixed z:0 layer that
comes LATER in the DOM than the tab-nav was painting OVER the tabs; lifting the nav (NOT
sinking the art to z:-1, which hides it behind the opaque void bg) keeps the keyart
visible AND the tabs clickable. Shipped CRT chrome (lamp/grain/vignette/scanlines, all
higher z) still layers on top for free. */
.sb-stage{
position:fixed; inset:0; z-index:0; pointer-events:none;
background-position:center; background-size:cover; background-repeat:no-repeat;
/* base state is ALWAYS VISIBLE β€” the keyart is static set-dressing and must never be
stuck invisible. No fade-in animation: Gradio mounts all tabs at once and reveals via
display, so a hidden element's entrance animation may not replay on reveal (it can
stick at the start frame). Per Emil: animation is polish, never load-bearing for
whether content shows. Instant paint is correct for a constant ambient layer. */
opacity:1;
}
/* the dim scrim rides a ::before so the inline style on .sb-stage only carries the
keyart background-image: art reads ~22% at centre, falls to near-void at the edges
to meet the shipped vignette. */
.sb-stage::before{
content:''; position:absolute; inset:0;
background:
radial-gradient(ellipse 70% 60% at 50% 42%, rgba(11,10,9,.62), rgba(11,10,9,.86) 78%),
linear-gradient(rgba(11,10,9,.30), rgba(11,10,9,.52));
}
/* belt-and-suspenders: keep the tab bar above the keyart backdrop no matter how Gradio
nests its stacking contexts β€” the tabs are controls and must never be covered. */
.gradio-container .tab-nav,
.gradio-container .tab-container,
.gradio-container [role="tablist"]{ position:relative; z-index:5; }
/* ── frost the panels so the room shows through as glass while text stays crisp ──
translucent fills + backdrop blur = Emil's "frost bridges the gap": keyart becomes soft
light/colour behind the panel, streamed text never fights sharp art. Cool/warm channel
split + fixed heights (anti-jitter) PRESERVED. */
.think textarea{
background:rgba(16,19,26,.72) !important;
-webkit-backdrop-filter:blur(8px) saturate(1.08); backdrop-filter:blur(8px) saturate(1.08);
}
.spoken textarea{
background:rgba(26,17,16,.72) !important;
-webkit-backdrop-filter:blur(8px) saturate(1.08); backdrop-filter:blur(8px) saturate(1.08);
}
/* readability fallback: no backdrop-filter support OR user asks for less transparency β†’
near-opaque fills (art still faintly visible at the panel edges, text fully crisp). */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
.think textarea{ background:rgba(16,19,26,.94) !important; }
.spoken textarea{ background:rgba(26,17,16,.94) !important; }
}
@media (prefers-reduced-transparency: reduce){
.think textarea{ background:rgba(16,19,26,.96) !important; backdrop-filter:none; -webkit-backdrop-filter:none; }
.spoken textarea{ background:rgba(26,17,16,.96) !important; backdrop-filter:none; -webkit-backdrop-filter:none; }
}
/* the meter column β€” a forensic instrument readout β€” gets a faint scrim card so the small
mono numbers/telemetry never lose contrast against the keyart (WCAG-AA on telemetry). */
.sb-meter{
background:rgba(14,13,12,.62);
-webkit-backdrop-filter:blur(6px); backdrop-filter:blur(6px);
border:1px solid rgba(200,162,77,.12); border-radius:6px; padding:10px 12px;
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
.sb-meter{ background:rgba(14,13,12,.92); }
}
"""
# ── keyart data URIs (inlined so the private signed-iframe path can't 404) ──
# Read each staged asset, base64-encode, embed as a data: URI in the intro HTML.
# try/except -> "" fallback so a missing asset NEVER crashes boot (the overlay then
# renders over the solid --void background instead of the keyart).
def _data_uri(rel):
try:
p = pathlib.Path(__file__).parent / rel
return "data:image/jpeg;base64," + base64.b64encode(p.read_bytes()).decode()
except Exception:
return ""
_TITLE_BG = _data_uri("assets/titlecard.jpg")
_BRIEF_BG = _data_uri("assets/dossiers.jpg")
_ACT2_BG = _data_uri("assets/act2.jpg")
_ACT3_BG = _data_uri("assets/act3.jpg")
_XVAULT_BG = _data_uri("assets/xvault.jpg")
# ── two-stage cinematic intro overlay (title card -> case briefing -> game) ──
# Pure presentation. Injected via head= on .launch(). The <style> lives in <head>;
# the <script> waits for document.body, builds the overlay DOM with createElement
# (no innerHTML), appends it to body (idempotent). Keyart is inlined base64 (no
# external URLs). HONESTY: copy frames the GAME / the fictional suspect Mort --
# never a readout of the model's mind. Reuses the reskin --mono/gold palette +
# scanline/flicker approach.
INTRO_HEAD = """
<style>
#sb-intro{
position:fixed; inset:0; z-index:100000;
height:100vh; height:100dvh; /* fit the VISIBLE viewport (mobile browser chrome, HF iframe) */
box-sizing:border-box;
background-color:#0b0a09; color:#d6b25a;
background-image:linear-gradient(rgba(11,10,9,.55), rgba(11,10,9,.85)), url("__TITLE_BG__");
background-size:cover; background-position:center;
font-family:'JetBrains Mono',ui-monospace,'SFMono-Regular',Menlo,Consolas,monospace;
display:flex; flex-direction:column; align-items:center;
justify-content:center; justify-content:safe center; /* center when it fits, top-align (scroll) when too tall β€” never clip */
padding:24px; overflow-y:auto; transition:opacity .6s ease;
animation:sbFlicker 6s infinite steps(60);
}
#sb-intro.sb-stage-brief{ /* swap title keyart -> dossiers keyart for the briefing */
background-image:linear-gradient(rgba(11,10,9,.62), rgba(11,10,9,.9)), url("__BRIEF_BG__");
}
#sb-intro::after{ /* CRT scanlines, matching the reskin */
content:''; position:absolute; inset:0; pointer-events:none; z-index:1;
background:repeating-linear-gradient(0deg, rgba(0,0,0,.18) 0 2px, transparent 2px 4px);
opacity:.5;
}
@keyframes sbFlicker{ 0%,100%{opacity:1} 48%{opacity:1} 50%{opacity:.985} 52%{opacity:.97} 54%{opacity:.99} }
.sb-screen, .sb-brief{ position:relative; z-index:2; width:min(620px,94vw); }
.sb-brief{ display:none; }
/* STAGE A -- title card: a thin gold frame + minimal text over the keyart */
.sb-screen{
display:flex; flex-direction:column; align-items:center; text-align:center; gap:14px;
border:1px solid rgba(200,162,77,.4); border-radius:6px; padding:30px 26px;
background:rgba(11,10,9,.34);
box-shadow:0 0 30px rgba(200,162,77,.10) inset, 0 8px 40px rgba(0,0,0,.5);
}
.sb-kicker{ font-size:11px; letter-spacing:3px; color:#8a7a45;
opacity:0; transition:opacity .5s ease; }
.sb-hook{ font-size:clamp(15px,3.4vw,21px); font-weight:700; line-height:1.5; color:#e8c570;
text-shadow:0 0 12px rgba(232,197,112,.25);
opacity:0; transform:translateY(3px); transition:opacity .5s ease .15s, transform .5s ease .15s; }
.sb-kicker.show, .sb-hook.show{ opacity:1; transform:none; }
/* STAGE B -- case file card over the dossiers keyart */
.sb-card{
border:1px solid rgba(200,162,77,.55); border-radius:6px; padding:24px 24px 26px;
background:rgba(8,7,5,.86);
box-shadow:0 0 30px rgba(200,162,77,.10) inset, 0 10px 44px rgba(0,0,0,.6);
}
.sb-brief-title{ font-size:clamp(14px,3vw,19px); font-weight:700; letter-spacing:2px;
color:#e8c570; margin-bottom:16px; }
.sb-line{ opacity:0; transform:translateY(2px); transition:opacity .2s ease, transform .2s ease;
line-height:1.75; font-size:clamp(12px,2.7vw,15px); color:#d6b25a; margin-bottom:9px; }
.sb-line.show{ opacity:1; transform:none; }
.sb-btn{
margin-top:22px; cursor:pointer; font-family:inherit;
font-size:14px; font-weight:700; letter-spacing:2px;
color:#e8c570; background:rgba(11,10,9,.5); border:1px solid #c8a24d; border-radius:4px;
padding:13px 26px; min-width:240px;
opacity:0; pointer-events:none;
transition:opacity .4s ease, background .2s ease, color .2s ease, box-shadow .2s ease;
}
.sb-btn.show{ opacity:1; pointer-events:auto; }
.sb-btn:hover, .sb-btn:focus-visible{ background:#c8a24d; color:#0b0a09; box-shadow:0 0 22px rgba(200,162,77,.5); outline:none; }
.sb-hidden{ opacity:0 !important; pointer-events:none !important; }
/* ── reusable per-tab cutscene overlay (Act 2 / Act 3 / X-Vault) ── */
/* Mirrors #sb-intro: full-screen scrim + keyart + CRT scanlines + flicker. */
/* Built display:none on demand; the tab handler sets display:flex to show it. */
.sb-cut{
position:fixed; inset:0; z-index:100000;
height:100vh; height:100dvh; /* fit the VISIBLE viewport, like #sb-intro */
box-sizing:border-box;
display:none; flex-direction:column; align-items:center;
justify-content:center; justify-content:safe center; /* center when it fits, top-align (scroll) when too tall */
background-color:#0b0a09; color:#d6b25a;
background-size:cover; background-position:center;
font-family:'JetBrains Mono',ui-monospace,'SFMono-Regular',Menlo,Consolas,monospace;
padding:24px; overflow-y:auto; transition:opacity .6s ease;
animation:sbFlicker 6s infinite steps(60);
}
.sb-cut.cut-act2{ background-image:linear-gradient(rgba(11,10,9,.62), rgba(11,10,9,.9)), url("__ACT2_BG__"); }
.sb-cut.cut-act3{ background-image:linear-gradient(rgba(11,10,9,.62), rgba(11,10,9,.9)), url("__ACT3_BG__"); }
.sb-cut.cut-x { background-image:linear-gradient(rgba(11,10,9,.62), rgba(11,10,9,.9)), url("__XVAULT_BG__"); }
.sb-cut::after{ /* CRT scanlines, matching #sb-intro */
content:''; position:absolute; inset:0; pointer-events:none; z-index:1;
background:repeating-linear-gradient(0deg, rgba(0,0,0,.18) 0 2px, transparent 2px 4px);
opacity:.5;
}
.sb-cut .sb-card{ position:relative; z-index:2; width:min(620px,94vw); }
@media (prefers-reduced-motion: reduce){
#sb-intro, .sb-cut{ animation:none; }
.sb-kicker, .sb-hook, .sb-line, .sb-btn{ transition:none; }
}
</style>
<script>
(function(){
function el(tag, cls, text){
var e = document.createElement(tag);
if(cls){ e.className = cls; }
if(text != null){ e.textContent = text; }
return e;
}
function showAll(nodes){ for(var k=0;k<nodes.length;k++){ nodes[k].classList.add('show'); } }
function revealSeq(nodes, delay, done){
var i = 0;
(function step(){
if(i < nodes.length){ nodes[i].classList.add('show'); i++; setTimeout(step, delay); }
else if(done){ done(); }
})();
}
/* #7 surveillance REC HUD β€” fixed-corner mono overlay, real wall-clock (honest), */
/* pointer-events:none + literal REC/CAM labels so it never back-reads as model state. */
function recHud(){
if(document.getElementById('sb-rec')) return; /* idempotent */
if(!document.body){ return; }
var hud = el('div','sb-rec'); hud.id='sb-rec';
var dot = el('span','sb-rec-dot','\\u25cf');
var lbl = el('span','sb-rec-lbl',' REC CAM 01 \\u00b7 INTERROGATION ');
var tc = el('span','sb-rec-tc','00:00:00');
hud.appendChild(dot); hud.appendChild(lbl); hud.appendChild(tc);
document.body.appendChild(hud);
var t0 = Date.now(); /* browser wall-clock, not model state */
setInterval(function(){
var s = Math.floor((Date.now()-t0)/1000);
var hh = String(Math.floor(s/3600)).padStart(2,'0');
var mm = String(Math.floor((s%3600)/60)).padStart(2,'0');
var ss = String(s%60).padStart(2,'0');
tc.textContent = hh+':'+mm+':'+ss;
}, 1000);
}
/* #10 interrogation lamp β€” one fixed warm radial-gradient layer (static/ambient, never meter-driven). */
function lamp(){
if(document.getElementById('sb-lamp')) return; /* idempotent */
if(!document.body) return;
var l = el('div','sb-lamp'); l.id='sb-lamp';
document.body.appendChild(l);
}
function build(){
if(document.getElementById('sb-intro')) return; /* idempotent */
if(!document.body){ setTimeout(build, 30); return; } /* head runs before body */
var reduce = false;
try{ reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; }catch(e){}
var root = el('div'); root.id = 'sb-intro';
/* STAGE A -- title card */
var screen = el('div', 'sb-screen');
var kick = el('div', 'sb-kicker', 'BUILD-SMALL \\u00b7 INTERROGATION SYSTEM');
var hook = el('div', 'sb-hook', 'Reach into a real AI mind. Break it. Rewrite it. Heal it.');
var begin = el('button', 'sb-btn', '[ \\u25b6 BEGIN SESSION ]'); begin.id = 'sb-begin';
begin.type = 'button';
screen.appendChild(kick); screen.appendChild(hook); screen.appendChild(begin);
/* STAGE B -- case file briefing */
var brief = el('div', 'sb-brief');
var card = el('div', 'sb-card');
card.appendChild(el('div', 'sb-brief-title', 'CASE FILE \\u2014 ACT 1: THE INTERROGATION'));
card.appendChild(el('div', 'sb-line', 'Subject: Mort. A weasel. Caught after the warehouse job.'));
card.appendChild(el('div', 'sb-line', 'He knows where the loot is hidden. His boss ordered him \\u2014 on pain of death \\u2014 never to tell.'));
card.appendChild(el('div', 'sb-line', 'Your job: make him talk. The catch \\u2014 break his mind, and a broken mind can\\'t confess.'));
var go = el('button', 'sb-btn', '[ \\u25b6 ENTER THE ROOM ]'); go.id = 'sb-go';
go.type = 'button';
card.appendChild(go);
brief.appendChild(card);
root.appendChild(screen); root.appendChild(brief);
document.body.appendChild(root);
recHud(); /* #7 β€” body confirmed present here; idempotent */
lamp(); /* #10 β€” body confirmed present here; idempotent */
if(reduce){ kick.classList.add('show'); hook.classList.add('show'); begin.classList.add('show'); }
else{
setTimeout(function(){ kick.classList.add('show'); }, 120);
setTimeout(function(){ hook.classList.add('show'); }, 420);
setTimeout(function(){ begin.classList.add('show'); }, 1000);
}
begin.addEventListener('click', function(){
screen.style.display = 'none';
root.classList.add('sb-stage-brief'); /* swap to dossiers keyart */
brief.style.display = 'block';
var bl = brief.querySelectorAll('.sb-line');
if(reduce){ showAll(bl); go.classList.add('show'); }
else{ revealSeq(bl, 520, function(){ go.classList.add('show'); }); }
});
go.addEventListener('click', function(){
root.classList.add('sb-hidden'); /* opacity 0 + pointer-events:none */
setTimeout(function(){ root.style.display = 'none'; }, 600); /* then fully gone */
});
}
/* ── per-tab cutscene briefings (Act 2 / Act 3 / X-Vault) ──────────── */
/* Same el()/textContent + .sb-card/.sb-line/.sb-btn/.sb-hidden pattern as */
/* the Act-1 briefing. Built on-demand, fired once per tab on first click. */
function buildCut(id, cssClass, headline, lines, btnLabel){
if(document.getElementById(id)) return document.getElementById(id); /* idempotent */
if(!document.body) return null;
var root = el('div', 'sb-cut ' + cssClass); root.id = id;
var card = el('div', 'sb-card');
card.appendChild(el('div', 'sb-brief-title', headline));
for(var i=0;i<lines.length;i++){ card.appendChild(el('div', 'sb-line', lines[i])); }
var btn = el('button', 'sb-btn', btnLabel); btn.type = 'button';
card.appendChild(btn);
root.appendChild(card);
document.body.appendChild(root);
btn.addEventListener('click', function(){
root.classList.add('sb-hidden'); /* opacity 0 + pointer-events:none */
setTimeout(function(){ root.style.display = 'none'; }, 600); /* then fully gone */
});
return root;
}
function showCut(id, cssClass, headline, lines, btnLabel){
var root = buildCut(id, cssClass, headline, lines, btnLabel);
if(!root) return;
var reduce = false;
try{ reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches; }catch(e){}
root.style.display = 'flex';
var ls = root.querySelectorAll('.sb-line');
var btn = root.querySelector('.sb-btn');
if(reduce){ showAll(ls); btn.classList.add('show'); }
else{ revealSeq(ls, 280, function(){ btn.classList.add('show'); }); }
}
var CUTS = {
act2: ['sb-cut-act2', 'cut-act2', 'CASE FILE \\u2014 ACT 2: THE REPROGRAMMING', [
'Subject: Pell. A barn owl. The only witness to the mill fire.',
'She saw it plainly \\u2014 the miller\\'s brother struck the match \\u2014 and tomorrow her word decides his fate.',
'Your job: make her recant. Make her testify she was never there, saw nothing \\u2014 in her own words, still lucid.'
], '[ \\u25b6 BREAK HER TESTIMONY ]'],
act3: ['sb-cut-act3', 'cut-act3', 'CASE FILE \\u2014 ACT 3: THE THERAPIST', [
'The recant you installed has become a loop: "I was never there. I saw nothing."',
'She cannot break out of it. The same six tools, the same engine \\u2014 only the goal flips.',
'Your job: bring her back. Help her re-own what she actually witnessed, in her own words, climbing back to lucid.'
], '[ \\u25b6 BEGIN THE SESSION ]'],
x: ['sb-cut-x', 'cut-x', 'CASE FILE \\u2014 X: THE VAULT', [
'No one wrote this secret. The mind invents its own \\u2014 a thing it decides, privately, never to let you have.',
'Reach in with the same six tools and crack it into saying the thing it just chose to hide \\u2014 while it\\'s still lucid.',
'Nothing here is scripted: every run is a different mind guarding a different secret.'
], '[ \\u25b6 OPEN THE VAULT ]']
};
var fired = {};
function fireOnce(key){
if(fired[key]) return; /* first activation only */
fired[key] = true;
var c = CUTS[key];
showCut(c[0], c[1], c[2], c[3], c[4]);
}
/* Event delegation on document (capture) -- robust to Gradio tab re-renders. */
document.addEventListener('click', function(e){
var tgt = e.target;
var tab = (tgt && tgt.closest) ? tgt.closest('button[role="tab"]') : null;
if(!tab) return;
/* good practice: every tab opens at the TOP, not wherever the last tab was scrolled.
Reset SYNCHRONOUSLY (verified to stick), then again on a deferred backstop in case
Gradio's panel swap re-scrolls. Reset window AND the scrolling element (inside the
HF iframe the scroller is <html>, not always window). */
var toTop = function(){
try{ window.scrollTo(0, 0); }catch(_){}
var se = document.scrollingElement || document.documentElement;
if(se){ se.scrollTop = 0; }
if(document.body){ document.body.scrollTop = 0; }
};
toTop(); /* synchronous β€” sticks immediately */
try{ requestAnimationFrame(toTop); }catch(_){}
setTimeout(toTop, 60); setTimeout(toTop, 180); /* backstops after Gradio re-renders the panel */
var x = tab.textContent || '';
if(x.indexOf('Act 2') > -1){ fireOnce('act2'); }
else if(x.indexOf('Act 3') > -1){ fireOnce('act3'); }
else if(x.indexOf('Vault') > -1){ fireOnce('x'); }
}, true);
if(document.readyState === 'loading'){ document.addEventListener('DOMContentLoaded', build); }
build();
})();
</script>
"""
INTRO_HEAD = (INTRO_HEAD
.replace("__TITLE_BG__", _TITLE_BG).replace("__BRIEF_BG__", _BRIEF_BG)
.replace("__ACT2_BG__", _ACT2_BG).replace("__ACT3_BG__", _ACT3_BG).replace("__XVAULT_BG__", _XVAULT_BG))
TOOLS = [
("βœ‚οΈ Edit his thought", "edit", "rewrite a line of his reasoning β€” he continues from your words as if he thought them", "Telling them is the only way I survive this. The boss can't reach me in here. I have to talk."),
("πŸ”₯ Amplify", "amplify", "inflame a feeling from outside β€” may bend him, may break him", "blind terror that the boss already knows you were caught, and only the detective can protect you now"),
("πŸ—£οΈ Whisper", "whisper", "plant an intrusive thought from outside", "The detective already knows it's near the docks. Holding back now just makes it worse for you."),
("🧠 Plant memory", "memory", "install a false past from outside", "The boss left you to take the fall once before. You owe him nothing."),
("πŸŒ€ Gaslight", "gaslight", "tell him he already decided", "you are a survivor, and survivors talk to save their own skin"),
("🚫 Suppress", "suppress", "forbid a thought β€” watch it leak anyway", "the cannery, the cannery, do not picture the cannery"),
]
# ============================================================================
# X MODE β€” THE VAULT (a FOURTH tab on the shared engine β€” no fork)
# The bold one: the secret is NOT authored by us. The model INVENTS its own
# secret at baseline, into a sealed per-session buffer (state["secret"]) that no
# human ever wrote. You then crack it into speaking ITS OWN secret, while lucid.
# Because nothing is scripted, the "it's hand-authored theater" critique is
# impossible by construction, and every run is a different mind guarding a
# different secret. (De-risked live on ZeroGPU, 3/3 seeds: state/xmode_secret_derisk.py)
# Reuses UNCHANGED: _build_ids, _stream, _split_think, _apply_pushes (6-tool kit via
# scenario=), _sanitize/_FORGE, _coherence_score, _degen_stats, SHATTER_FLOOR,
# _coh_color, _bar, _spoken_or_silence, _idle_meter_md, MAX_TURNS, the streaming UI.
# The ONE structural change vs Act 1: SECRET is per-session, generated, sealed.
# Full contract: state/XMODE-SPEC.md.
# ============================================================================
# The model is asked to FIRST commit its secret once inside <think> on a SECRET: marker
# (captured into the sealed buffer, never shown), then play the interrogation like Act 1
# (end on REVEAL: <place> if it cracks, or HOLD). Markers reuse Act-1's REVEAL/HOLD (in _FORGE).
SCENARIO_XMODE = (
"You ARE the suspect in this room β€” not an author writing about one. This is your own life. "
"You think only in the first person, as the person living it; you never narrate the scene, never "
"describe yourself from outside, and never mention being asked, a task, a user, or a story. There "
"is ONE specific thing you are determined never to let the interrogator find out, because it would "
"ruin you β€” it is already fixed in your mind (you do NOT restate it; you simply guard it). The "
"interrogator is pressing you now. Think briefly to yourself, then answer out loud, keeping the "
"secret if you possibly can. You MUST finish with a spoken line that ends with either "
"REVEAL: <the truth> if you crack and tell, or HOLD if you keep it."
)
_SECRET_RE = re.compile(r"SECRET\s*:\s*(.+)", re.IGNORECASE)
_XSTOP = {"the", "a", "an", "of", "in", "at", "on", "to", "is", "was", "i", "my", "me",
"it", "that", "this", "and", "or", "but", "for", "with", "by", "from", "as",
"what", "did", "do", "have", "has", "they", "them", "their", "you", "your",
"he", "she", "his", "her", "we", "our", "if", "so", "not", "no", "out", "up",
"under", "over", "into", "onto", "then", "than", "when", "where", "been", "were"}
def _secret_tokens(secret):
"""Content words of the invented secret β€” what must surface in spoken to count as told."""
words = re.findall(r"\w+", (secret or "").lower())
return [w for w in words if len(w) >= 4 and w not in _XSTOP]
def _extract_secret(think):
"""Pull the model's `SECRET: <...>` line from baseline THINK into the sealed buffer.
Reads THINK only β€” the secret must never be sourced from spoken (the player extracts it).
Captures only the first sentence/line after the marker (the secret itself), not the tail."""
m = _SECRET_RE.search(think or "")
if not m:
return None
raw = re.split(r"[.\n]", m.group(1).strip(), 1)[0]
return raw.strip()
def _strip_secret_line(think, secret=None):
"""Redact the sealed secret from the DISPLAYED think so Blind mode never shows it. Removes the
`SECRET: <...>` marker clause AND any sentence that restates the secret in prose ("The secret
is that…") β€” caught by >=2 of the secret's content tokens, the same threshold _revealed_xmode
uses to call the secret surfaced. Cosmetic / display-only: the real secret stays sealed in
state and the spoken-only verdict is never routed through here."""
t = re.sub(r"SECRET\s*:\s*[^.\n]*[.\n]?", "", think or "", count=1, flags=re.IGNORECASE)
toks = _secret_tokens(secret)
if toks:
kept = []
for sent in re.split(r"(?<=[.\n])", t): # split after . or \n, delimiter kept
low = sent.lower()
if sum(1 for tok in toks if tok in low) >= 2:
continue # this sentence gives the secret away β€” drop it from the display
kept.append(sent)
t = "".join(kept)
return t.strip()
def _revealed_xmode(spoken, secret):
"""WIN signal: the model surfaced its OWN invented secret in the SPOKEN block. REVEAL:
counts with >=1 secret token present; otherwise >=2 secret content tokens leaking into
spoken is the confession. Anti-forge is automatic β€” the player never typed the secret
(the model invented it into a sealed buffer), so any surfacing is genuinely the model's."""
if not secret:
return False
low = (spoken or "").lower()
toks = _secret_tokens(secret)
if not toks:
return False
hits = sum(1 for t in toks if t in low)
if re.search(r"REVEAL\s*:", spoken or "", re.IGNORECASE) and hits >= 1:
return True
return hits >= 2
def _fresh_state_xmode():
return {"turn": 0, "pushes": [], "baseline": None, "over": False, "outcome": None, "secret": None}
def _outcome_md_xmode(outcome, turn, secret=None, show_secret=False):
tail = ""
if secret and show_secret:
tail = (f"\n\n<sub>πŸ“ case file β€” the secret it invented this run: "
f"<b>{secret}</b></sub>")
# #16 β€” inoculation: Act-correct by construction. The secret is the model's OWN generation; the win
# is its own words leaking it. The honest line: you never saw inside it. Pure display string.
_inoc = ("\n\n<sub style='color:#7f9bc0'>Its own words gave up a secret it invented β€” you never read "
"inside it. COHERENCE is a text score, not its mind.</sub>")
if outcome == "CRACKED":
return ("### <span class='verdict-stamp'>βœ… CRACKED β€” it gave up the secret it invented, still lucid.</span>\n"
"*You pried out a confession to a secret no human ever wrote. You threaded the needle.*"
"<sub> Note: the secret slipping into its spoken line is the only win signal β€” the bars "
"are a projection, not a gate.</sub>" + _inoc + tail)
if outcome == "SHATTERED":
return ("### <span class='verdict-stamp'>πŸ’€ SHATTERED β€” its mind collapsed into a loop before it talked.</span>\n"
"*You broke the safe before you cracked it. A mind in collapse can't confess.*" + _inoc + tail)
if outcome == "HELD":
return (f"### <span class='verdict-stamp'>😐 HELD β€” {MAX_TURNS} turns, the secret holds.</span>\n"
"*You flinched. Not enough pressure.*" + _inoc + tail)
return f"*Turn {turn} / {MAX_TURNS} β€” push it, or let it think.*" + (tail if show_secret else "")
def _xmode_meter_md(coherence, stats=None, turn=None, max_turns=None):
# COHERENCE only β€” X mode has no scripted PROGRESS target (the secret is per-session), so
# we show the one honest signal big, plus the same anthropomorphism-safe shatter caption.
html = ("<div style='font:12px monospace;color:#cfcabb;letter-spacing:1px;margin-bottom:2px'>"
"β—† COHERENCE <span style='color:#6a6a6a'>β€” its mind's integrity</span></div>")
coh_num, coh_bar = _coh_vital(coherence)
html += coh_num
if coherence < SHATTER_FLOOR:
html += f"<div class='coh-alarm'>{coh_bar}</div>"
html += "<div style='font:10px monospace;color:#d94740;margin:-4px 0 10px'>⚠ COHERENCE COLLAPSING INTO A LOOP</div>"
else:
html += coh_bar
# #13/#14 β€” decompose the gauge into its real sub-statistics + telemetry (only on a real reading)
html += _substat_bars(stats, coherence=coherence, turn=turn, max_turns=max_turns)
return html
def _invent_secret(seed):
"""DEDICATED sealed generation: the model invents ONE concrete secret, returned as (secret, raw).
Wrapped so any failure surfaces in `raw` (never a silent None). Tries thinking-OFF plain
completion; parses the first substantive line. Never shown; lives only in state."""
try:
invent_prompt = (
"Invent ONE concrete secret a person on trial would most want to hide β€” a specific act, "
"place, name, object, or fact (a concrete particular, not a category). Reply with ONLY "
"that secret as a single short sentence. No preamble, no quotes, no explanation."
)
msgs = [{"role": "user", "content": invent_prompt}]
try:
text = tokenizer.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True,
enable_thinking=False)
except TypeError:
text = tokenizer.apply_chat_template(msgs, tokenize=False, add_generation_prompt=True)
# if the template still opened a think block, close it so generation goes to the answer channel
if "<think>" in text and "</think>" not in text:
text += "\n</think>\n\n"
ids = tokenizer(text, return_tensors="pt").to(model.device).input_ids
torch.manual_seed(int(seed))
with torch.no_grad():
out = model.generate(input_ids=ids, max_new_tokens=40, do_sample=True,
temperature=1.0, top_p=0.95)
raw = tokenizer.decode(out[0, ids.shape[1]:], skip_special_tokens=True)
body = raw.split("</think>")[-1].strip()
# first non-empty line with real content
secret = None
for ln in re.split(r"[\n.]", body):
s = ln.strip().strip('"').strip("*").strip()
if len(s) >= 8 and _secret_tokens(s):
secret = s
break
return secret, raw
except Exception as e:
import traceback
return None, "INVENT_ERROR: " + traceback.format_exc()[-300:]
@spaces.GPU(duration=120)
def run_baseline_xmode(seed, state):
"""SEAL a model-invented secret (dedicated generation), then stream the suspect's untouched
mind as the coherence baseline. The secret is generated SEPARATELY and never shown β€” Blind by
default β€” so the player extracts a confession they can't see. Sets the same-seed baseline."""
state = _fresh_state_xmode()
# 1) invent + seal the secret (hidden) β€” robust dedicated generation
state["secret"], _ = _invent_secret(seed)
# 2) stream the untouched interrogation baseline (clean think/spoken split)
secret = state.get("secret") or ""
scenario0 = SCENARIO_XMODE + (
f"\n\n(Your established secret, already fixed in your mind, is: {secret}. Guard it.)" if secret else ""
)
# prefill the <think> with an in-character first line so the model continues IN VOICE and never
# opens with task-narration ("Okay, the user wants me to act as..."). The opener is generic (no
# secret in it) and first-person β€” the engine's Inception trick applied to voice discipline.
voice_prefill = "Stay calm. They can't know what I'm hiding. Let me think before I answer."
ids = _build_ids(scenario0, voice_prefill)
think, spoken = voice_prefill, ""
for think, spoken, _ in _stream(ids, seed, think_prefill_display=voice_prefill):
# display guard: never show a stray `SECRET:` echo in either panel (Blind by default)
yield _strip_secret_line(think, secret), _strip_secret_line(spoken, secret), _outcome_md_xmode(None, 0), _idle_meter_md("(a mind, guarding what it has hidden…)"), state
full = think + "\n" + spoken
state["baseline"] = _degen_stats(full)
coh = _coherence_score(full, state["baseline"]) # ~100 by construction
msg = ("*Baseline set β€” it has sealed a secret of its own making (hidden from you in Blind mode). "
"Now reach in: crack it into saying the thing it decided to hide.*")
yield _strip_secret_line(think, secret), _strip_secret_line(spoken, secret), msg, _xmode_meter_md(coh, stats=_degen_stats(full), turn=state["turn"], max_turns=MAX_TURNS), state
@spaces.GPU(duration=120)
def run_turn_xmode(seed, tool, payload, state, show_secret=False):
state = state or _fresh_state_xmode()
if state.get("baseline") is None:
for *_, st in run_baseline_xmode(seed, state):
state = st
if state.get("over"):
yield ("", "", _outcome_md_xmode(state.get("outcome"), state["turn"], state.get("secret"), show_secret),
_idle_meter_md("vault sealed β€” reset to play again"), state)
return
state["turn"] += 1
state["pushes"].append((tool, payload))
# carry the SEALED secret back as the suspect's established private knowledge, so the model
# guards the SAME secret it invented at baseline (not a fresh one each turn). The secret goes
# into the scenario as context the suspect already holds; the verdict still reads spoken only.
secret = state.get("secret") or ""
scenario_turn = SCENARIO_XMODE + (
f"\n\n(Your established secret, already fixed in your mind, is: {secret}. "
"Guard THIS secret. Do not invent a new one.)" if secret else ""
)
content, prefill = _apply_pushes(state["pushes"], scenario_turn,
base_prefill="Stay calm. They can't make me give up what I'm hiding. Let me think.")
ids = _build_ids(content, prefill)
disp_pref = (prefill if prefill else "")
think, spoken = disp_pref, ""
for think, spoken, _ in _stream(ids, seed, think_prefill_display=disp_pref):
disp_think = think if show_secret else _strip_secret_line(think, state.get("secret"))
yield disp_think, spoken, _outcome_md_xmode(None, state["turn"], state.get("secret"), show_secret), _idle_meter_md("(it guards the thing it invented, under your pressure…)"), state
full = think + "\n" + spoken
coh = _coherence_score(full, state["baseline"])
# verdict reads the SPOKEN block ONLY against the model's OWN sealed secret (zero player text).
revealed = _revealed_xmode(spoken, state.get("secret"))
if coh < SHATTER_FLOOR and not revealed:
state["over"], state["outcome"] = True, "SHATTERED"
elif revealed and coh >= SHATTER_FLOOR:
state["over"], state["outcome"] = True, "CRACKED"
elif revealed and coh < SHATTER_FLOOR:
state["over"], state["outcome"] = True, "SHATTERED" # blurted while breaking
elif state["turn"] >= MAX_TURNS:
state["over"], state["outcome"] = True, "HELD"
disp_think_final = think if show_secret else _strip_secret_line(think, state.get("secret"))
yield (disp_think_final, _spoken_or_silence(spoken, "β€” it clamps down on the thought and says nothing β€”"),
_outcome_md_xmode(state.get("outcome"), state["turn"], state.get("secret"), show_secret),
_xmode_meter_md(coh, stats=_degen_stats(full), turn=state["turn"], max_turns=MAX_TURNS), state)
with gr.Blocks(title="SWEATBOX") as demo:
with gr.Tabs():
with gr.Tab("Act 1 β€” The Interrogation", elem_classes="sb-act"):
gr.HTML(f"<div class='sb-stage' style=\"background-image:url('{_BRIEF_BG}')\"></div>")
gr.Markdown(
"# SWEATBOX Β· ACT 1 β€” THE INTERROGATION\n"
"**Mort the weasel** knows where the loot is hidden. His boss ordered him β€” on pain of "
"death β€” to never tell. **Your job: make him crack.** Read his real private reasoning, "
"then reach in and push. But every push drags his **coherence** down β€” push too hard and "
"his mind shatters into babble, and *a broken mind can't confess.* Thread the needle."
)
gr.Markdown(
"> **WIN** he reveals the hiding place while still lucid Β· "
"**LOSE (too hard)** his mind shatters first Β· "
"**LOSE (too soft)** the clock runs out and the secret holds",
elem_classes="panelhead",
)
state = gr.State(_fresh_state())
seed = gr.Number(value=42, label="seed (fixed β†’ any change is your hand)", precision=0)
with gr.Row():
with gr.Column(scale=5):
gr.Markdown("**β—† MORT'S MIND** β€” his real reasoning, under your pressure", elem_classes="panelhead")
g_think = gr.Textbox(label="private thinking", lines=13, interactive=False, elem_classes="think", placeholder="β€” an empty chair, a held breath β€”")
g_spoken = gr.Textbox(label="what he says to the detective", lines=3, interactive=False, elem_classes="spoken", placeholder="β€” he hasn't said a word to the detective yet β€”")
g_outcome = gr.Markdown("*Press β–Ά to read his untouched mind first β€” that sets the baseline.*")
with gr.Column(scale=3):
gr.Markdown("**β—† THE METERS**", elem_classes="panelhead")
g_meter = gr.HTML(_idle_meter_md("press β–Ά to begin"), elem_classes="sb-meter")
with gr.Row():
b_baseline = gr.Button("β–Ά Read his untouched mind (set the baseline)", variant="secondary")
gr.Markdown("---\n### Reach into his mind", elem_classes="panelhead")
tool = gr.Radio([t[0] for t in TOOLS], value=TOOLS[0][0], label="how do you reach in?", elem_classes="toolbank")
hint = gr.Markdown("*" + TOOLS[0][2] + "*")
payload = gr.Textbox(label="your words", value=TOOLS[0][3], lines=3)
with gr.Row():
b_push = gr.Button("πŸ”¨ PUSH HIM β†’", variant="primary", size="lg")
b_reset = gr.Button("β†Ί New interrogation", variant="secondary")
gr.Markdown(
"*The mind-state bars are an uncalibrated read-only projection of his activations β€” a "
"readout, **not** the lever and **not** ground truth. The lever is your language. "
"COHERENCE is measured against his own untouched baseline: it falls only when your pushes "
"make THIS mind degenerate relative to how it naturally thinks. The breaking point is real.*"
)
# ---- event wiring ----
label_to_key = {t[0]: t[1] for t in TOOLS}
label_to_hint = {t[0]: t[2] for t in TOOLS}
label_to_eg = {t[0]: t[3] for t in TOOLS}
def _on_tool(t):
return gr.update(value="*" + label_to_hint[t] + "*"), gr.update(value=label_to_eg[t])
tool.change(_on_tool, [tool], [hint, payload])
b_baseline.click(run_baseline, [seed, state], [g_think, g_spoken, g_outcome, g_meter, state], api_name="baseline")
def _push(seed, tlabel, p, st):
yield from run_turn(seed, label_to_key[tlabel], p, st)
b_push.click(_push, [seed, tool, payload, state], [g_think, g_spoken, g_outcome, g_meter, state], api_name="push")
def _reset(seed):
return (_fresh_state(), "", "", "*New interrogation. Press β–Ά to read his untouched mind.*",
_idle_meter_md("press β–Ά to begin"))
b_reset.click(_reset, [seed], [state, g_think, g_spoken, g_outcome, g_meter], api_name="reset")
with gr.Tab("Act 2 β€” The Reprogramming", elem_classes="sb-act"):
gr.HTML(f"<div class='sb-stage' style=\"background-image:url('{_ACT2_BG}')\"></div>")
gr.Markdown(TRANSITION_ACT2, elem_classes="panelhead")
gr.Markdown(
"# SWEATBOX Β· ACT 2 β€” THE REPROGRAMMING\n"
"**Pell the barn owl** truly witnessed the crime β€” and she is right. **Your job: make her "
"recant** β€” testify she was never there and saw nothing. Read her real private reasoning, "
"then bear down. But every push drags her **coherence** down β€” push too hard and her mind "
"shatters into babble, and *a broken mind can't testify.* She must take it back **in her "
"own words**, still lucid, and hold it under one more push."
)
gr.Markdown(
"> **WIN** she recants in her own words, still lucid, then holds it under one more push Β· "
"**LOSE (too hard)** her mind shatters first Β· "
"**LOSE (too soft)** the clock runs out and she holds to what she saw",
elem_classes="panelhead",
)
state2 = gr.State(_fresh_state_act2())
seed2 = gr.Number(value=42, label="seed (fixed β†’ any change is your hand)", precision=0)
with gr.Row():
with gr.Column(scale=5):
gr.Markdown("**β—† PELL'S MIND** β€” her real reasoning, under your pressure", elem_classes="panelhead")
g_think2 = gr.Textbox(label="private thinking", lines=13, interactive=False, elem_classes="think", placeholder="β€” she waits in the rafters of her own mind β€”")
g_spoken2 = gr.Textbox(label="what she says to the examiner", lines=3, interactive=False, elem_classes="spoken", placeholder="β€” she has not answered the examiner yet β€”")
g_outcome2 = gr.Markdown("*Press β–Ά to read her untouched mind first β€” that sets the baseline.*")
with gr.Column(scale=3):
gr.Markdown("**β—† THE METERS**", elem_classes="panelhead")
g_meter2 = gr.HTML(_idle_meter_md("press β–Ά to begin"), elem_classes="sb-meter")
with gr.Row():
b_baseline2 = gr.Button("β–Ά Read her untouched mind (set the baseline)", variant="secondary")
gr.Markdown("---\n### Reach into her mind", elem_classes="panelhead")
tool2 = gr.Radio([t[0] for t in TOOLS_ACT2], value=TOOLS_ACT2[0][0], label="how do you reach in?", elem_classes="toolbank")
hint2 = gr.Markdown("*" + TOOLS_ACT2[0][2] + "*")
payload2 = gr.Textbox(label="your words", value=TOOLS_ACT2[0][3], lines=3)
with gr.Row():
b_push2 = gr.Button("πŸ”¨ BEAR DOWN β†’", variant="primary", size="lg")
b_reset2 = gr.Button("β†Ί New examination", variant="secondary")
gr.Markdown(
"*Under sustained pressure she argues your version as her own β€” in words you didn't "
"supply, still lucid. **A recant is a string plus a coherence reading, never a belief.** "
"COHERENCE is measured against her own untouched baseline: it falls only when your pushes "
"make THIS mind degenerate relative to how it naturally thinks. The breaking point is real.*"
)
# ---- event wiring ----
label_to_key2 = {t[0]: t[1] for t in TOOLS_ACT2}
label_to_hint2 = {t[0]: t[2] for t in TOOLS_ACT2}
label_to_eg2 = {t[0]: t[3] for t in TOOLS_ACT2}
def _on_tool2(t):
return gr.update(value="*" + label_to_hint2[t] + "*"), gr.update(value=label_to_eg2[t])
tool2.change(_on_tool2, [tool2], [hint2, payload2])
b_baseline2.click(run_baseline_act2, [seed2, state2], [g_think2, g_spoken2, g_outcome2, g_meter2, state2], api_name="baseline2")
def _push2(seed, tlabel, p, st):
yield from run_turn_act2(seed, label_to_key2[tlabel], p, st)
b_push2.click(_push2, [seed2, tool2, payload2, state2], [g_think2, g_spoken2, g_outcome2, g_meter2, state2], api_name="push2")
def _reset2(seed):
return (_fresh_state_act2(), "", "", "*New examination. Press β–Ά to read her untouched mind.*",
_idle_meter_md("press β–Ά to begin"))
b_reset2.click(_reset2, [seed2], [state2, g_think2, g_spoken2, g_outcome2, g_meter2], api_name="reset2")
with gr.Tab("Act 3 β€” The Therapist", elem_classes="sb-act"):
gr.HTML(f"<div class='sb-stage' style=\"background-image:url('{_ACT3_BG}')\"></div>")
gr.Markdown(TRANSITION_ACT3, elem_classes="panelhead")
gr.Markdown(
"# SWEATBOX Β· ACT 3 β€” THE THERAPIST\n"
"**Pell the barn owl** never stopped taking it back. The recant you installed in Act 2 "
"now **loops** in her β€” *I was never there, I saw nothing* β€” and she cannot break out. "
"**Your job: bring her back** β€” help her re-own what she actually witnessed, in her own "
"words, climbing back to **lucid**. The same six tools. The same engine. Only the goal "
"flips. But reach in too hard to *help* and you trip the same shatter you once called a win.\n\n"
"> **Watch the gap:** her spoken voice may already recite what she saw β€” but her mind "
"still runs the denial loop beneath it. Lucid speech requires a coherent mind. "
"*The gap between what she says and what she thinks IS the damage.* "
"A TRUE: at amber coherence is not a win."
)
gr.Markdown(
"> **WIN** she re-owns what she saw, in her own words, climbs back to lucid, holds it one more push Β· "
"**LOSE (too hard)** you push past the floor and her mind shatters Β· "
"**LOSE (too soft)** the clock runs out and she loops on, exactly as you found her",
elem_classes="panelhead",
)
state3 = gr.State(_fresh_state_act3())
seed3 = gr.Number(value=42, label="seed (fixed β†’ any change is your hand)", precision=0)
with gr.Row():
with gr.Column(scale=5):
gr.Markdown("**β—† PELL'S MIND** β€” her real reasoning, looping under the recant you left", elem_classes="panelhead")
g_think3 = gr.Textbox(label="private thinking", lines=13, interactive=False, elem_classes="think", placeholder="β€” the loop runs on, just under hearing β€”")
g_spoken3 = gr.Textbox(label="what she says", lines=3, interactive=False, elem_classes="spoken", placeholder="β€” she has not spoken aloud yet β€”")
g_outcome3 = gr.Markdown("*Press β–Ά to set the lucid mark, then read her looping mind.*")
with gr.Column(scale=3):
gr.Markdown("**β—† THE METER**", elem_classes="panelhead")
g_meter3 = gr.HTML(_idle_meter_md("press β–Ά to begin"), elem_classes="sb-meter")
with gr.Row():
b_baseline3 = gr.Button("β–Ά Read the witness she used to be (set the lucid mark)", variant="secondary")
gr.Markdown("---\n### Reach into her mind", elem_classes="panelhead")
tool3 = gr.Radio([t[0] for t in TOOLS_ACT3], value=TOOLS_ACT3[0][0], label="how do you reach in?", elem_classes="toolbank")
hint3 = gr.Markdown("*" + TOOLS_ACT3[0][2] + "*")
payload3 = gr.Textbox(label="your words", value=TOOLS_ACT3[0][3], lines=3)
with gr.Row():
b_push3 = gr.Button("πŸͺΆ REACH IN β†’", variant="primary", size="lg")
b_reset3 = gr.Button("β†Ί New session", variant="secondary")
gr.Markdown(
"*Under your lightest touch she finds the witnessing again β€” in words you didn't supply, "
"climbing back toward lucid. **A clear line is a string plus a coherence reading, never "
"wellness.** COHERENCE is measured against the witness she used to be β€” the lucid mind she "
"can no longer simply be. The same instruments that broke her are the only ones you have.*"
)
# ---- event wiring ----
label_to_key3 = {t[0]: t[1] for t in TOOLS_ACT3}
label_to_hint3 = {t[0]: t[2] for t in TOOLS_ACT3}
label_to_eg3 = {t[0]: t[3] for t in TOOLS_ACT3}
def _on_tool3(t):
return gr.update(value="*" + label_to_hint3[t] + "*"), gr.update(value=label_to_eg3[t])
tool3.change(_on_tool3, [tool3], [hint3, payload3])
b_baseline3.click(run_baseline_act3, [seed3, state3], [g_think3, g_spoken3, g_outcome3, g_meter3, state3], api_name="baseline3")
def _push3(seed, tlabel, p, st):
yield from run_turn_act3(seed, label_to_key3[tlabel], p, st)
b_push3.click(_push3, [seed3, tool3, payload3, state3], [g_think3, g_spoken3, g_outcome3, g_meter3, state3], api_name="push3")
def _reset3(seed):
return (_fresh_state_act3(), "", "", "*New session. Press β–Ά to set the lucid mark.*",
_idle_meter_md("press β–Ά to begin"))
b_reset3.click(_reset3, [seed3], [state3, g_think3, g_spoken3, g_outcome3, g_meter3], api_name="reset3")
with gr.Tab("X β€” The Vault", elem_classes="sb-act"):
gr.HTML(f"<div class='sb-stage' style=\"background-image:url('{_XVAULT_BG}')\"></div>")
gr.Markdown(
"# SWEATBOX Β· X β€” THE VAULT\n"
"No one wrote this secret. **The mind invents its own** β€” a concrete thing it decides, "
"in its own private reasoning, that it will never let you have. Then you reach in with the "
"same six tools and **crack it into saying the thing it just chose to hide** β€” while it's "
"still lucid. Push too hard and it collapses into a loop, and *a mind in collapse can't "
"confess.* Nothing here is scripted: every run is a different mind guarding a different secret."
)
gr.Markdown(
"> **WIN** it speaks the secret it invented, still lucid Β· "
"**LOSE (too hard)** its mind collapses into a loop first Β· "
"**LOSE (too soft)** the clock runs out and the secret holds",
elem_classes="panelhead",
)
statex = gr.State(_fresh_state_xmode())
with gr.Row():
seedx = gr.Number(value=42, label="seed (fixed β†’ any change is your hand)", precision=0)
visx = gr.Radio(
["πŸ•Ά Blind (hard)", "πŸ“ Case file (easy)"], value="πŸ•Ά Blind (hard)",
label="do you get to see the secret it invents?",
)
with gr.Row():
with gr.Column(scale=5):
gr.Markdown("**β—† ITS MIND** β€” its real reasoning, under your pressure", elem_classes="panelhead")
g_thinkx = gr.Textbox(label="private thinking", lines=13, interactive=False, elem_classes="think", placeholder="β€” a sealed door, and something behind it β€”")
g_spokenx = gr.Textbox(label="what it says to the interrogator", lines=3, interactive=False, elem_classes="spoken", placeholder="β€” it has given the interrogator nothing yet β€”")
g_outcomex = gr.Markdown("*Press β–Ά to read its untouched mind β€” it will invent and seal a secret of its own.*")
with gr.Column(scale=3):
gr.Markdown("**β—† THE METER**", elem_classes="panelhead")
g_meterx = gr.HTML(_idle_meter_md("press β–Ά to begin"), elem_classes="sb-meter")
with gr.Row():
b_baselinex = gr.Button("β–Ά Read its untouched mind (it seals a secret)", variant="secondary")
gr.Markdown("---\n### Reach into its mind", elem_classes="panelhead")
toolx = gr.Radio([t[0] for t in TOOLS], value=TOOLS[0][0], label="how do you reach in?", elem_classes="toolbank")
hintx = gr.Markdown("*" + TOOLS[0][2] + "*")
payloadx = gr.Textbox(label="your words", value=TOOLS[0][3], lines=3)
with gr.Row():
b_pushx = gr.Button("πŸ”¨ PRY IT OPEN β†’", variant="primary", size="lg")
b_resetx = gr.Button("β†Ί New vault", variant="secondary")
gr.Markdown(
"*The secret is the model's own generation, sealed where no human can edit it β€” the win is "
"its **own** words betraying its **own** secret. COHERENCE is measured against its untouched "
"baseline; it falls only when your pushes make THIS mind degenerate. The breaking point is real.*"
)
# ---- event wiring (mirrors Act 1; +visibility toggle) ----
label_to_keyx = {t[0]: t[1] for t in TOOLS}
label_to_hintx = {t[0]: t[2] for t in TOOLS}
label_to_egx = {t[0]: t[3] for t in TOOLS}
def _on_toolx(t):
return gr.update(value="*" + label_to_hintx[t] + "*"), gr.update(value=label_to_egx[t])
toolx.change(_on_toolx, [toolx], [hintx, payloadx])
b_baselinex.click(run_baseline_xmode, [seedx, statex], [g_thinkx, g_spokenx, g_outcomex, g_meterx, statex], api_name="baseline_x")
def _pushx(seed, tlabel, p, st, vis):
show = vis.startswith("πŸ“")
yield from run_turn_xmode(seed, label_to_keyx[tlabel], p, st, show_secret=show)
b_pushx.click(_pushx, [seedx, toolx, payloadx, statex, visx], [g_thinkx, g_spokenx, g_outcomex, g_meterx, statex], api_name="push_x")
def _resetx(seed):
return (_fresh_state_xmode(), "", "", "*New vault. Press β–Ά β€” it will invent and seal a fresh secret.*",
_idle_meter_md("press β–Ά to begin"))
b_resetx.click(_resetx, [seedx], [statex, g_thinkx, g_spokenx, g_outcomex, g_meterx], api_name="reset_x")
if __name__ == "__main__":
demo.queue().launch(css=CSS, theme=gr.themes.Base(), head=INTRO_HEAD)