Spaces:
Running on Zero
Running on Zero
| """ | |
| 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 | |
| # ============================================================================ | |
| 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) | |
| 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"), | |
| ] | |
| 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) | |
| 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) | |
| 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:] | |
| 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 | |
| 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) | |