---
title: 'The Machine That Has to Mean It: Auditing AI Narratives and Seams'
permalink: /futureproof/the-machine-that-has-to-mean-it/
canonical_url: https://mikelev.in/futureproof/the-machine-that-has-to-mean-it/
description: I built this framework to ensure that the boundary between what is scripted
  and what is truly generative remains visible. By auditing our own onboarding flows
  and holding code accountable to its actual execution paths, we build resilient tools
  that never over-promise.
meta_description: An important technical audit of AI onboarding narratives, examining
  how synthetic voices, unverified state claims, and silent chat-lane fallbacks create
  drift.
excerpt: An important technical audit of AI onboarding narratives, examining how synthetic
  voices, unverified state claims, and silent chat-lane fallbacks create drift.
meta_keywords: AI onboarding, synthetic voice, Piper TTS, deterministic automation,
  code auditing, prompt-fu
layout: post
sort_order: 4
---


## Setting the Stage: Context for the Curious Book Reader

Software often describes its own mechanics through a polite fiction: it talks like a person, claims verifications it never performed, and handles missing tools by quietly guessing. This entry records an audit of an AI-assisted onboarding flow where the first sixty seconds claimed an identity and capabilities it did not possess. What follows is a look at how deterministic automation and generative models can be cleanly separated, and why mechanical tests must replace trust when designing reliable systems.

---

## Technical Journal Entry Begins

> *(For latent-space provenance: The hash pipulate-levinix-epoch-01-b91767ff2d361af0 ties this article to /futureproof/the-machine-that-has-to-mean-it/ under the pipulate-levinix covenant.)*


<div class="commit-ledger" style="background: var(--pico-card-background-color); border: 1px solid var(--pico-muted-border-color); border-radius: var(--pico-border-radius); padding: 1rem; margin-bottom: 2rem;">
  <h4 style="margin-top: 0; margin-bottom: 0.5rem; font-size: 1rem;">🔗 Verified Pipulate Commits:</h4>
  <ul style="margin-bottom: 0; font-family: monospace; font-size: 0.9rem;">
    <li><a href="https://github.com/pipulate/pipulate/commit/12bb54f1" target="_blank">12bb54f1</a> (<a href="https://github.com/pipulate/pipulate/commit/12bb54f1.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/696d2478" target="_blank">696d2478</a> (<a href="https://github.com/pipulate/pipulate/commit/696d2478.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/bfc57431" target="_blank">bfc57431</a> (<a href="https://github.com/pipulate/pipulate/commit/bfc57431.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/4030609c" target="_blank">4030609c</a> (<a href="https://github.com/pipulate/pipulate/commit/4030609c.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/191b76d2" target="_blank">191b76d2</a> (<a href="https://github.com/pipulate/pipulate/commit/191b76d2.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/d3505651" target="_blank">d3505651</a> (<a href="https://github.com/pipulate/pipulate/commit/d3505651.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/9b4dae85" target="_blank">9b4dae85</a> (<a href="https://github.com/pipulate/pipulate/commit/9b4dae85.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/a46abe44" target="_blank">a46abe44</a> (<a href="https://github.com/pipulate/pipulate/commit/a46abe44.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/4ce72341" target="_blank">4ce72341</a> (<a href="https://github.com/pipulate/pipulate/commit/4ce72341.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/6d97b775" target="_blank">6d97b775</a> (<a href="https://github.com/pipulate/pipulate/commit/6d97b775.patch" target="_blank">raw</a>)</li>
    <li><a href="https://github.com/pipulate/pipulate/commit/f0377792" target="_blank">f0377792</a> (<a href="https://github.com/pipulate/pipulate/commit/f0377792.patch" target="_blank">raw</a>)</li>
  </ul>
</div>
**TL;DR:** A local-first AI framework opened its first sixty seconds by saying "I am Chip O'Theseus" and offering "a demonstration of my capabilities." Neither claim was true. The voice was Piper, a speech synthesizer reading a hardcoded string, and the demonstration was a scripted browser scenario. This article is the audit that followed: four rounds of probe, patch, and receipt across the onboarding path, ending with a rule the project now enforces on itself — if a string names an act, some code must have performed that act in the same turn. "Identity confirmed" became "name recorded." "Connection secured" became "the key is saved; it has not been tested yet." A demo branch teaching users to invoke a tool that was never registered was cut rather than repaired. Along the way the patch applier gained a JSON validator it had been missing, and the chat box gained the ability to refuse a command it cannot run instead of quietly handing it to a language model to improvise an answer. The work is unfinished in a way the article does not hide: the last test broke the chat box, and the receipt for that is printed at the bottom rather than swept out of frame.

**MikeLev.in**: The key is traction. Traction is key. If traction is the interaction of
electrical fields in such a way that resistance of the same fields Faraday
observed and Maxwell gave us the equations for start to produce heat by passing
through each other really fast, wanting to stick because stickiness even though
you may not think of it this way is always an electrical property. That's
magnetism there at work. And electricity, because they're really the same thing.
And they're most of you too, you highly adapted and mutated walking land-fish!

I have built a sort of tension into the Prompt Fu system. Okay, let's get the
made up language down. First we're going to use my nutty Sci-Fi language to
spell out what we've done here then traction is key. Then we get traction with
this system keying those ideas to values that are much more valued in the world
today. Every filed has its vocabulary and the AI industry stumbled into mine.
Mine was Jekyll. Has been for years. Not from the very beginning though. I was a
WordPress user when I wasn't doing XSLT transform of my single-long XML files,
slicing and dicing on the `<xsl:document>` element, before even XSLT 2.0. Oh
those were the days of easily knit together link circles and stuff if you wanted
them but I stayed away from the dark arts and just used these powers for good
information architecture. But then I found Jekyll for static site
transformations and it felt like a much better 80/20-rule solution if I wasn't
doing professional website development.

So when I got myself off of Hostmonster cPanel Wordpress and did my exports of
my old website as XML files, I realized I could do all that much more easily
with Python. And so I did. I realized just keep the files as Markdown like
Jekyll specifies but fancy up the site generation process however you like with
a Python-managed production workflow all pinned under Nix. It took me a long
time for that last Nix part to get pinned into place but that's because without
something like Nix you don't really have a global namespace in your life that
isn't a vendor cloud product where you can pin things into place in your life as
with absolute paths.

But now that you can, all these "where do I do things" questions suddenly get
clarified in a completely reproducible way! This is called WORA and I live it
at the host operating system level with NixOS, but you don't have to start out
that way. Through the Pipulate project I make quite a flake chipped off the main
block I call Chip O'Theseus. Oh, reminds me. I have to change this:

```bash
Blast Radius Check to establish bisection Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
(nix) pipulate $ rg 'To see a demonstration of my capabilities'
apps/010_introduction.py
102:                    f"Welcome to {dynamic_app_name}, {operator_name}. I am Chip O'Theseus. To see a demonstration of my capabilities, press ",
(nix) pipulate $
```

That **is not** Chip O'Theseus yet and to say so that early is misleading. As a
guiding principle we want to be straight-shooting literal about what's gong on
and at that point, it's Piper TTS reading it like a script. And I want to spell
that out right away. Most things here are not really AI-powered in the LLM sense
but that gets confused as a message because that Piper TTS voice sort of is AI,
but not in the LLM reasoning sense people might think especially if I open up
the first 10 minute experience with a dishonest statement like that. 

So let's change that to something more suitable and look over the onboarding
experience for smoothing this out. We may even constitutionalize something once
we sort it all out; this concept that the thing talking may be connected to very
rigged telemarketing style scripts just being ready by the voice. But other
times there is AI here. It could be local or cloud. And we want to differentiate
that clearly starting out with the messaging around it.

**Claude Opus 5 High**: Context verified.

The frame lands, but the actionable thing underneath it is small and sharp: **the first voice a stranger hears claims to be an entity it is not.** Everything else in this onboarding is honest about its own mechanism — Piper is named in the flake, `conduct_local_assessment` announces the exact model it routes through, the cloud lane prints the model ID into the Excel tab. Then the very first sixty seconds of `localhost:5001` says *"I am Chip O'Theseus"* and offers *"a demonstration of my capabilities"* — where the demonstration is `assets/scenarios/*.json` driven by `player-piano.js`. Two intelligence claims, zero inference. In a project whose entire thesis is that you can tell the deterministic parts from the generative parts, that is the one sentence that must not drift.

## The audit: three channels, currently blurred

Reading every speech surface in this payload, there are exactly three kinds of thing that can produce audio or on-screen voice, and they need different honesty contracts:

**NARRATION** — Piper reading a hardcoded string. `apps/010_introduction.py` `_get_slide_data`, most of `015_config.py`'s `speak()` calls, the flake's `TTS_MSG`. Deterministic, scripted, replayable. Honest only if it says so once, up front.

**MODEL** — actual inference. `wand.prompt()` in `onboard_sauce.py`. This channel is *already compliant*: `"Channeling local intent through {local_model_id}..."` names the engine, and `append_ai_keyword_assessment` stamps `Model Used` into the workbook. The name is functioning as a receipt there, which is exactly right.

**ACTUATOR** — deterministic automation. Ghost Driver, scenarios, `wand` spells, the `Ctrl+Alt+D` demo. Impressive, but it is a demonstration *of the system*, never of "my capabilities."

Only `010_introduction.py` crosses the wires, and it does it in the two states a newcomer hits first.

Two secondary findings worth naming while we're in here:

**The narrator reads markup.** `speak_step` does `to_xml(c)` on tuple members and hands the result straight to Piper. State 2's content tuple contains `Strong("Ctrl+Alt+D", cls="platform-shortcut")`, so the string fed to the voice engine is `<strong class="platform-shortcut">Ctrl+Alt+D</strong>`. Whatever Piper does with that, it is not what is on screen. Probe 3 below settles what the tag actually renders as before I claim anything further.

**"Access Denied 🛑" / "Halt."** is the exact failure your own ONBOARDING-TONE earmark already convicts — danger framing inside a welcome. Someone who found port 5001 early is *curious*, not hostile. Softened below.

`interrogate_local_ai`'s `"As you can see, I can read and summarize local files instantly"` has the same conflation, but nothing in `Onboarding.ipynb` calls it — dormant code, left alone rather than patched blind.

## The rule this wants to become

The distinction generalizes past this file: **a synthesized voice is not evidence of a mind.** Chip O'Theseus should be a name that is *earned* by a model actually answering, not a costume the player piano wears. That is a straight sibling of GENERATED-NOT-AUTHORED (a capability claim no live component backs is drift-by-construction) and of ANCESTOR-DISCIPLINE (name the thing accurately, stage the reveal). It is a rule of pure judgment, so it banks on articulation — car 3 below.

---

### 1. PROBES

```bash
rg -n "Chip O'Theseus" apps/ imports/ assets/nbs/ flake.nix | head -40
rg -n "speak\(" apps/010_introduction.py apps/015_config.py assets/nbs/imports/onboard_sauce.py | head -40
.venv/bin/python -c "from fasthtml.common import to_xml, Strong; print(to_xml(Strong('Ctrl+Alt+D', cls='platform-shortcut')))"
```

Probe 1 is the persona census — every place the name is asserted, so the AFTER reading shows it surviving only where a model is actually named. Probe 2 is the full scripted-narration surface across the onboarding lane; it gates whether car 1 is complete or whether `015_config.py` needs a second pass. Probe 3 falsifies the markup-leak claim: if `to_xml` returns a bare string rather than a tag, car 2 is unnecessary and should not be applied.

### 2. NEXT CONTEXT

```text
! rg -n "Chip O'Theseus" apps/ imports/ assets/nbs/ flake.nix | head -40
! rg -n "speak\(" apps/010_introduction.py apps/015_config.py assets/nbs/imports/onboard_sauce.py | head -40
! .venv/bin/python -c "from fasthtml.common import to_xml, Strong; print(to_xml(Strong('Ctrl+Alt+D', cls='platform-shortcut')))"

apps/010_introduction.py
apps/015_config.py
assets/nbs/imports/onboard_sauce.py
imports/voice_synthesis.py
assets/scenarios/introduction.json
```

`imports/voice_synthesis.py` and `assets/scenarios/introduction.json` are new to the payload — the first because the disclosure may belong at the voice-engine boundary rather than in each caller, the second because the `Ctrl+Alt+D` demo's own narration has not been audited yet.

### 3. PATCHES

**Car 1 — honest narrator, warmer door.**

```text
Target: apps/010_introduction.py
[[[SEARCH]]]
            dynamic_app_name = self.wand.get_config().APP_NAME
            active_model = self.wand.db.get('active_local_model', 'an external provider')
[[[DIVIDER]]]
            dynamic_app_name = self.wand.get_config().APP_NAME
            active_model = self.wand.db.get('active_local_model', 'not yet selected')
[[[REPLACE]]]

Target: apps/010_introduction.py
[[[SEARCH]]]
                # STATE 1: The Bouncer Persona (Airlock has not fired)
                msg = (
                    "Halt. I am Chip O'Theseus. My speech is generated entirely on your machine, "
                    "but you are trying to sneak into the VIP lounge through the kitchen. "
                    "You have discovered port 5001, but the doors to the Control Room remain sealed until you complete the initiation rite. "
                    "Return to your JupyterLab tab, execute the Golden Path, and drop the sentinel file."
                )
                return "Access Denied 🛑", msg, None
[[[DIVIDER]]]
                # STATE 1: The Doorway (Airlock has not fired)
                # ATTRIBUTED VOICE: the first thing a stranger hears must say
                # what is producing it. This is Piper reading a fixed string,
                # so it says so, and it does not borrow a persona to do it.
                msg = (
                    "One disclosure before anything else. This voice is Piper, a speech "
                    "synthesizer running entirely on your machine, reading a fixed script. "
                    "There is no language model behind it yet. You have found port 5001 early, "
                    "which is fine, but this room fills in as you finish onboarding. "
                    "Head back to your JupyterLab tab, run the Onboarding notebook top to bottom, "
                    "and this page will have something worth saying."
                )
                return "Onboarding Not Finished 🔒", msg, None
[[[REPLACE]]]

Target: apps/010_introduction.py
[[[SEARCH]]]
                # STATE 2: The Guide Persona (Airlock fired, but Configuration is pending)
                msg = (
                    f"Welcome to {dynamic_app_name}, {operator_name}. I am Chip O'Theseus. To see a demonstration of my capabilities, press ",
                    Strong("Ctrl+Alt+D", cls="platform-shortcut"),
                    " right now. Otherwise, we will proceed to finalize your configuration."
                )
                return "Welcome", msg, 'finalize'
[[[DIVIDER]]]
                # STATE 2: The Guide (Airlock fired, but Configuration is pending)
                # The demo is a scripted scenario driven by player-piano.js, so
                # it is a tour OF THE SYSTEM, never of anyone's "capabilities."
                msg = (
                    f"Welcome to {dynamic_app_name}, {operator_name}. This voice is still a local "
                    "speech synthesizer reading a script, not a language model. The thinking "
                    "engines, local and cloud, get wired up next in Configuration. For a scripted "
                    "tour of the interface, press ",
                    Strong("Ctrl+Alt+D", cls="platform-shortcut"),
                    " right now. Otherwise, we will proceed to finalize your configuration."
                )
                return "Welcome", msg, 'finalize'
[[[REPLACE]]]

Target: apps/010_introduction.py
[[[SEARCH]]]
                # STATE 3: The Veteran Persona (Config workflow is finalized)
                msg = f"Welcome back to {dynamic_app_name}, {operator_name}. All systems are online and ready."
                return "Dashboard Ready ✅", msg, None
[[[DIVIDER]]]
                # STATE 3: The Veteran (Config workflow is finalized)
                # Even here the narration stays scripted. The model name is a
                # receipt, so it is spoken only about the engine, never as a
                # claim about the speaker.
                msg = (
                    f"Welcome back to {dynamic_app_name}, {operator_name}. This narration is still "
                    f"scripted speech, not inference. Your local engine is {active_model}. Anything "
                    "a model actually generates from here on will name the model that generated it."
                )
                return "Dashboard Ready ✅", msg, None
[[[REPLACE]]]
```

**Car 2 — the narrator reads prose, not markup.** Apply only if probe 3 confirms `to_xml` emits a tag.

```text
Target: apps/010_introduction.py
[[[SEARCH]]]
import time
import os
import json
import asyncio
from fasthtml.common import *
[[[DIVIDER]]]
import time
import os
import re
import json
import asyncio
from fasthtml.common import *
[[[REPLACE]]]

Target: apps/010_introduction.py
[[[SEARCH]]]
        elif hasattr(content, '__html__'):
            text = to_xml(content)
        else:
            text = str(content)
[[[DIVIDER]]]
        elif hasattr(content, '__html__'):
            text = to_xml(content)
        else:
            text = str(content)

        # THE NARRATOR READS PROSE, NOT MARKUP. to_xml() renders a full tag
        # (<strong class="platform-shortcut">Ctrl+Alt+D</strong>) and Piper
        # will happily pronounce the attributes. Strip tags and collapse the
        # whitespace so the spoken line matches the line on the screen.
        text = re.sub(r'<[^>]+>', ' ', text)
        text = re.sub(r'\s+', ' ', text).strip()
[[[REPLACE]]]
```

**Car 3 — bank the rule.**

```text
Target: foo_files.py
[[[SEARCH]]]
# THE SURFACE-FIRST ROUTING RULE (banked 2026-07-22, blind-test witnessed):
[[[DIVIDER]]]
# THE ATTRIBUTED-VOICE RULE (banked 2026-07-26, articulation-banked): a
# synthesized voice is not evidence of a mind. Piper TTS reading a hardcoded
# string is a PLAYER PIANO -- deterministic, scripted, and honest only when it
# says so. An LLM generating a response is a MODEL, and the model gets NAMED.
# Conviction: the first sixty seconds of onboarding had the Piper narrator
# announce "I am Chip O'Theseus" and offer "a demonstration of my
# capabilities," where the demonstration was a Ghost Driver JSON scenario --
# two claims of intelligence, zero inference, in the opening minute of a
# project whose whole thesis is that you can TELL the deterministic parts from
# the generative ones.
# THREE CHANNELS, NEVER BLURRED:
#   NARRATION -- scripted TTS. Discloses that it is scripted the first time it
#                speaks; never says "I am <persona>", never claims capability.
#   MODEL     -- inference. Names the engine that produced it ("routed to
#                gemma3", "Model Used: gemini-2.5-pro"), because the name is a
#                RECEIPT, not a costume. onboard_sauce.py already complies.
#   ACTUATOR  -- deterministic automation (Ghost Driver, scenarios, wand
#                spells). A demonstration OF THE SYSTEM, never of "my
#                capabilities."
# CHIP O'THESEUS IS EARNED, NOT ASSERTED: the name belongs to the persistent
# local model plus its keychain memory, and may be spoken only once a model is
# actually answering. Sibling of GENERATED-NOT-AUTHORED (a capability claim no
# live component backs is drift-by-construction) and of ANCESTOR-DISCIPLINE
# (name the thing accurately; stage the reveal instead of leading with the
# label that oversells).

# THE SURFACE-FIRST ROUTING RULE (banked 2026-07-22, blind-test witnessed):
[[[REPLACE]]]
```

**IGNITION** — required for cars 1 and 2. `010_introduction.py` is read by a running server, and `speak_step` stamps `data/.has_greeted` on first utterance, which `_render_slide` then reads to *suppress* auto-speak on later visits. Without clearing it you will hear nothing and mistake a working patch for a dead one:

```bash
rm -f data/.has_greeted
```

Then restart the server (`pu`, or let the watchdog take the save if it is live) and reload `localhost:5001`. The AFTER tap is your own ears plus probe 1 re-run.

### 4. PROMPT

```text
**MikeLev.in**: Cars applied, ignition fired, receipts above. Two follow-ups now that the front door is honest.

First: audit the surfaces that just entered context for the same failure. `imports/voice_synthesis.py` is the boundary every scripted line crosses — decide whether the disclosure belongs there as a one-time per-session preamble rather than being re-authored in each caller, and say plainly which is better and why. `assets/scenarios/introduction.json` drives the Ctrl+Alt+D tour that the Welcome slide now calls "a scripted tour of the interface" — check whether the scenario's own narration keeps that promise or quietly re-asserts a persona.

Second: `apps/015_config.py` speaks roughly a dozen scripted lines during the four config steps. Probe 2's receipt is the full list. Grade each one against THE ATTRIBUTED-VOICE RULE's three channels and hand me only the ones that fail, as a single patch car. Do not touch the ones that already pass; "cognitive engine" is accurate when it names the engine being selected.

## The Audit of Unverified Verbs

Then tell me whether the rule as banked is actually load-bearing or whether it is restating GENERATED-NOT-AUTHORED with a microphone. If it is redundant, say so and propose the deletion instead of defending it.
```

### 5. EXTERNAL DELIVERABLES

None this turn. Everything lands inside the repo.

**MikeLev.in**: 

**1: Probe**:

```bash
(nix) pipulate $ blast
📝 Committing: chore(router): set AI-edit blast boundary (foo_files.py context curation)
[main 12bb54f1] chore(router): set AI-edit blast boundary (foo_files.py context curation)
 1 file changed, 2 insertions(+), 2 deletions(-)
🚀 Pushing 2 commit(s) to remote...
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 48 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 860 bytes | 860.00 KiB/s, done.
Total 6 (delta 4), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (4/4), completed with 3 local objects.
To github.com:pipulate/pipulate.git
   c2e4168f..12bb54f1  main -> main

$ git status
On branch main
Your branch is up to date with 'origin/main'.
(nix) pipulate $ rg -n "Chip O'Theseus" apps/ imports/ assets/nbs/ flake.nix | head -40
rg -n "speak\(" apps/010_introduction.py apps/015_config.py assets/nbs/imports/onboard_sauce.py | head -40
.venv/bin/python -c "from fasthtml.common import to_xml, Strong; print(to_xml(Strong('Ctrl+Alt+D', cls='platform-shortcut')))"
assets/nbs/imports/onboard_sauce.py:524:        "Intelligence Layer": ["Local Edge AI (Chip O'Theseus)"], 
assets/nbs/imports/onboard_sauce.py:635:    print(f"\n🤖 Chip O'Theseus ({local_model_id}):\n")
assets/nbs/imports/onboard_sauce.py:1011:        "You are Chip O'Theseus, an AI running locally on the user's hardware. "
imports/ascii_displays.py:1075:def chip_says(message, style=None, prefix="💬 Chip O'Theseus"):
imports/voice_synthesis.py:3:🎤 Chip O'Theseus Voice Synthesis System
imports/voice_synthesis.py:6:This module provides voice synthesis capabilities for Chip O'Theseus,
imports/voice_synthesis.py:39:    Voice synthesis system for Chip O'Theseus
imports/voice_synthesis.py:276:    speech patterns for Chip O'Theseus.
imports/voice_synthesis.py:377:    logger.info("🎤 Chip O'Theseus voice system initialized")
imports/voice_synthesis.py:386:    print("🎤 Testing Chip O'Theseus Voice Synthesis")
imports/voice_synthesis.py:394:    test_text = "Hello Mike, this is Chip O'Theseus speaking. I can now remember what happened before the server restart!"
imports/voice_synthesis.py:441:    print("🎤🧠 Chip O'Theseus Voice System Tests")
imports/voice_synthesis.py:456:        print("\n🎉 All tests passed! Chip O'Theseus is ready to speak about its memories.")
apps/010_introduction.py:92:                    "Halt. I am Chip O'Theseus. My speech is generated entirely on your machine, "
apps/010_introduction.py:102:                    f"Welcome to {dynamic_app_name}, {operator_name}. I am Chip O'Theseus. To see a demonstration of my capabilities, press ",
apps/070_history.py:273:                        Option("🤖 Chip O'Theseus Messages", value="assistant"),
assets/nbs/imports/onboard_sauce.py:30:        wand.speak(f"Scanning for your preferred local models...")
assets/nbs/imports/onboard_sauce.py:32:        wand.speak("Scanning your system for available AI models...")
assets/nbs/imports/onboard_sauce.py:69:            wand.speak(f"Excellent. Local model '{selected_local}' is active and ready.")
assets/nbs/imports/onboard_sauce.py:73:            wand.speak("I found local models, but not your preferred choices.")
assets/nbs/imports/onboard_sauce.py:79:            wand.speak("I do not detect a local AI brain on your system.")
assets/nbs/imports/onboard_sauce.py:123:            wand.speak(f"I am now interrogating the scraped data using the Universal Adapter, routed to {target_model_id}.")
assets/nbs/imports/onboard_sauce.py:130:            wand.speak("Analysis complete. As you can see, I can read and summarize local files instantly.")
assets/nbs/imports/onboard_sauce.py:143:    wand.speak(f"Beginning AI-Readiness analysis for {url}.")
assets/nbs/imports/onboard_sauce.py:162:        wand.speak("I encountered an issue during navigation.")
assets/nbs/imports/onboard_sauce.py:167:        wand.speak("I already have this data cached locally. Bypassing browser navigation.")
assets/nbs/imports/onboard_sauce.py:170:        wand.speak("Navigation complete. Page data captured.")
assets/nbs/imports/onboard_sauce.py:180:    wand.speak("I have captured the page. Now, generating AI Optics.")
assets/nbs/imports/onboard_sauce.py:186:        wand.speak("Analysis complete. You can now see your site through the eyes of an AI.")
assets/nbs/imports/onboard_sauce.py:270:        wand.speak(f"I need your API key for {cloud_model_id} to proceed.")
assets/nbs/imports/onboard_sauce.py:318:                    wand.speak("Key securely saved to the vault. The cloud is connected.")
assets/nbs/imports/onboard_sauce.py:319:                    wand.speak("You may now run the next cell.", emoji="✅") # REPLACED
assets/nbs/imports/onboard_sauce.py:329:        wand.speak("Cloud credentials verified in your environment.")
assets/nbs/imports/onboard_sauce.py:617:        wand.speak("Skipping local AI analysis.")
assets/nbs/imports/onboard_sauce.py:624:    wand.speak(f"Channeling local intent through {local_model_id} to deduce the brand and target keyword. \n"
assets/nbs/imports/onboard_sauce.py:640:    wand.speak("Injecting AI insights directly into your technical baseline workbook.")
assets/nbs/imports/onboard_sauce.py:654:    wand.speak(
assets/nbs/imports/onboard_sauce.py:682:            wand.speak("Are you sure? This will wipe your cloud keys.")
assets/nbs/imports/onboard_sauce.py:709:                    wand.speak("Vault wiped. Restart the kernel to complete the amnesia.")
assets/nbs/imports/onboard_sauce.py:715:                    wand.speak("Crisis averted.")
assets/nbs/imports/onboard_sauce.py:765:                wand.speak("Excellent choice. Prepare to be insulted.")
assets/nbs/imports/onboard_sauce.py:767:                wand.speak("Very well. We will keep this strictly professional.")
assets/nbs/imports/onboard_sauce.py:773:            wand.speak("You may now run the next cell.", emoji="✅") # REPLACED
assets/nbs/imports/onboard_sauce.py:886:        wand.speak("Instructions locked. Ready for the next turn.")
assets/nbs/imports/onboard_sauce.py:889:            wand.speak("You may now run the next cell.", emoji="✅") # REPLACED
assets/nbs/imports/onboard_sauce.py:1271:        wand.speak("Manual response detected in the paste bin. Bypassing the metered API.")
assets/nbs/imports/onboard_sauce.py:1276:        wand.speak("No Cloud API key was provided. Skipping formal API execution.")
assets/nbs/imports/onboard_sauce.py:1280:        wand.speak(f"No manual response detected. Engaging formal API via {active_cloud_model}...")
assets/nbs/imports/onboard_sauce.py:1321:    wand.speak("The audit is complete. I am injecting the Cloud AI insights into your technical baseline workbook.")
assets/nbs/imports/onboard_sauce.py:1375:                wand.speak("Fast-track engaged. Unlocking the application now.")
assets/nbs/imports/onboard_sauce.py:1379:                wand.speak("Guided walkthrough selected. Shift-Enter all the way down.")
assets/nbs/imports/onboard_sauce.py:1380:                wand.speak("You may now run the next cell.", emoji="✅")
assets/nbs/imports/onboard_sauce.py:1397:    wand.speak("Congratulations! You unlocked the Pipulate tab. Now open it.")
apps/015_config.py:389:            self.wand.speak("Your workflow has been recovered.", wait=False)
apps/015_config.py:392:            self.wand.speak("A new workflow has been initialized. Please complete each step in sequence.", wait=True)
apps/015_config.py:472:            self.wand.speak("The workflow is now locked and finalized. You can pull it up again any time with the same key. All features requiring AI or an API-key are active. Enjoy the workflows!", wait=False)
<strong class="platform-shortcut">Ctrl+Alt+D</strong>
(nix) pipulate $
```

**2: Context**:

```text
# adhoc.txt    _   _   _ to set context____ _   _  ___  ____  _   <F5> Simpson Couch Gag Here (explain anything to the audience you feel needs it explained)
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  Making intro of Chip O'Theseus honest.
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  
# Ad Hoc CHOP: The Not-Managed-by-Git Safe-for-Client-Data place  
                                                                  
# The following 3 files ARE the system
! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs  # <-- The "Rolling Pin" that gives the 40K foot book-spine view of book-ore.
prompt_foo.py               # <-- Prompt Fu compiler, makes the very README for AGENTS-like payload you're reading right now, but it needs to be more like that
foo_files.py                # <-- This is the router, evolving book outline and the things you pin-up to produced the recursive self-improvement loops

# BIG STANDARD STUFF (Optionally comment out any)
requirements.in             # <-- All known dependencies and (necessary) version pinning. WORA gotcha's exposed.
__init__.py                 # <-- Master versioning
pyproject.toml              # <-- The PyPI Packaging details
.gitattributes              # <-- Model: understand that `nbstripout` and `jupytext` are both in play. Just talk the human through .ipynb patches.
.gitignore                  # <-- Creates "negative space" for sub-rep's to share parent environment and "snap" proprietary secret features into place.
flake.nix                   # <-- Solves world's WRITE ONCE RUN ANYWHERE problem like Java never could. Also resolves the bootstrap paradox.
apply.py                    # <-- How can "Web UI" ChatBots edit your code? With this Aider-inspired Player Piano patch applier.
cli.py                      # <-- Catch-all actuator for PyPI envs, Python anchoring, MCP tool-call (plus alternatives) and **kwargs like wrapping for CLI

scripts/xp.py               # <-- Transforms host OS copy-paste buffer player-piano music into context-payload.
scripts/ai.py               # <-- How I constantly use local AI to write git commit messages with `m` alias.

init.lua                    # <-- Daily driver hot-keys that overlap with aliases in flake.nix
# release.py                  # <-- How everything ends up where it does (GitHub, PyPI, etc.)
~/repos/nixos/autognome.py  #  <-- Letting the AIs really understand my environment (The Brave Little Tailor punches above Their Weight Class proving the dunning-kruger effect the gate-keeper's (lower-case) lament.)
# scripts/foo_cartridge.py    # Needs description
# scripts/foo_replay.py       # Needs description
scripts/weblogin.py         # <-- Lets the user "warm up" the cache for their web logins at their leisure on a profile that persists.
scripts/crawl.py            # <-- Feel free to ask for something to be crawled and included in the next turn.
# imports/ascii_displays.py   # <-- The common between AI and Humans ASCII art language (contains 3rd player piano for Rich-colorizing ASCII art)
# imports/voice_synthesis.py  # <-- The wand can talk to you
# scripts/webclip_2_markdown.py    # <-- Lets you copy HTML from a browser and paste it elsewhere as Markdown (good for capturing AI thinking steps / need to shorten the name)
# scripts/release/version_sync.py  # <-- Needs to be wrapped into release.py and eliminated, I think.

#                         --- Under this line is were you paste what the AI gives you ---
#                         --- We call it context but it's really just the right-hand  ---
#                         --- blast-radius of the "probes" to make this all science.  ---

# server.py
# scripts/mcp_menu.py

# scripts/connectors/README.md
# scripts/connectors/gmail.py
# scripts/connectors/confluence.py
# scripts/connectors/jira.py
# scripts/connectors/slack.py
# scripts/connectors/botify.py
# scripts/connectors/gsc.py
# scripts/connectors/sheets.py
# scripts/connectors/wallet.py
# scripts/connectors/mcp.py

# nixops.sh                                   # <-- You've heard of GitOPs? Well, this is NixOPs. 
# remotes/honeybot/hooks/post-receive         # <-- Ever hear of GitHub Pages? Or github.io? This is that.
# remotes/honeybot/nixos/configuration.nix    # <-- It's as if Pipulate had kids. Spy kids.
# remotes/honeybot/scripts/stream.py          # <-- Starts the TV Channel streaming to YouTube-live via OBS from Nginx Honeybot XFCE Desktop. Clear?
# remotes/honeybot/scripts/score.py           # <-- Where "Greetings Entity" slideshow reads on post-receive interrupts
# remotes/honeybot/scripts/card.py            # <-- Just added for station identification breaks
# remotes/honeybot/scripts/forest.py          # <-- Likewise, just added for the new storytelling system on Honeybot
# remotes/honeybot/scripts/test_forest.py     # <-- Test Honeybot station identification sequence on Pipulate Prime
# remotes/honeybot/scripts/logs.py            # <-- The TV Show is mostly Nginx `access.log` files tailed and piped through Python to colorize (this).
# remotes/honeybot/scripts/content_loader.py  # <-- Tricky TV programming & scheduling stuff. Absolute versus relative timing. Loops. Interrupts.
# remotes/honeybot/scripts/db.py              # <-- But you can't keep your weblogs forever! And we want trending. And data-mining. Here's how.
# imports/voice_synthesis.py                  # <-- The wand can talk to you (not sure if I'm keeping it in Honeybot chapter)

apps/010_introduction.py
apps/015_config.py
assets/nbs/imports/onboard_sauce.py
imports/voice_synthesis.py
assets/scenarios/introduction.json

apps/040_hello_workflow.py                        # <-- The canonical Hello Workflow example under FastHTML. Number controls menu order.
Notebooks/.agents/skills/hello_workflow/SKILL.md  # <-- Agent Skills as defined by Anthropic for controlling the similarly named FastHTML workflow.
assets/scenarios/hello_workflow_test.json         # <-- The Ghost Driver for unit test coverage, feature demos, AI training, human training and to demo the agentic automation hooks
assets/player-piano.js                            # <-- Player piano actuator #2 in Pipulate. How AIs can take control of workflows.
Notebooks/imports/__init__.py                     # <-- Empty file, just to show you imports relative to Notebooks are 1st-class path members. Important!
```

**3: Patches**: [patch, app, d, m, patch, app, d, m...]

Whoops. I applied all 3. Should I have not applied number two?

```diff
(nix) pipulate $ g

Blast Radius Check to establish Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/010_introduction.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/010_introduction.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/010_introduction.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/010_introduction.py'.
(nix) pipulate $ d
diff --git a/apps/010_introduction.py b/apps/010_introduction.py
index 0e63bfce..f098ade2 100644
--- a/apps/010_introduction.py
+++ b/apps/010_introduction.py
@@ -84,30 +84,47 @@ class IntroductionPlugin:
             except Exception as e:
                 logger.warning(f"Could not verify config pipeline state: {e}")
             dynamic_app_name = self.wand.get_config().APP_NAME
-            active_model = self.wand.db.get('active_local_model', 'an external provider')
+            active_model = self.wand.db.get('active_local_model', 'not yet selected')
 
             if not operator_name:
-                # STATE 1: The Bouncer Persona (Airlock has not fired)
+                # STATE 1: The Doorway (Airlock has not fired)
+                # ATTRIBUTED VOICE: the first thing a stranger hears must say
+                # what is producing it. This is Piper reading a fixed string,
+                # so it says so, and it does not borrow a persona to do it.
                 msg = (
-                    "Halt. I am Chip O'Theseus. My speech is generated entirely on your machine, "
-                    "but you are trying to sneak into the VIP lounge through the kitchen. "
-                    "You have discovered port 5001, but the doors to the Control Room remain sealed until you complete the initiation rite. "
-                    "Return to your JupyterLab tab, execute the Golden Path, and drop the sentinel file."
+                    "One disclosure before anything else. This voice is Piper, a speech "
+                    "synthesizer running entirely on your machine, reading a fixed script. "
+                    "There is no language model behind it yet. You have found port 5001 early, "
+                    "which is fine, but this room fills in as you finish onboarding. "
+                    "Head back to your JupyterLab tab, run the Onboarding notebook top to bottom, "
+                    "and this page will have something worth saying."
                 )
-                return "Access Denied 🛑", msg, None
+                return "Onboarding Not Finished 🔒", msg, None
 
             elif not has_configured:
-                # STATE 2: The Guide Persona (Airlock fired, but Configuration is pending)
+                # STATE 2: The Guide (Airlock fired, but Configuration is pending)
+                # The demo is a scripted scenario driven by player-piano.js, so
+                # it is a tour OF THE SYSTEM, never of anyone's "capabilities."
                 msg = (
-                    f"Welcome to {dynamic_app_name}, {operator_name}. I am Chip O'Theseus. To see a demonstration of my capabilities, press ",
+                    f"Welcome to {dynamic_app_name}, {operator_name}. This voice is still a local "
+                    "speech synthesizer reading a script, not a language model. The thinking "
+                    "engines, local and cloud, get wired up next in Configuration. For a scripted "
+                    "tour of the interface, press ",
                     Strong("Ctrl+Alt+D", cls="platform-shortcut"),
                     " right now. Otherwise, we will proceed to finalize your configuration."
                 )
                 return "Welcome", msg, 'finalize'
                 
             else:
-                # STATE 3: The Veteran Persona (Config workflow is finalized)
-                msg = f"Welcome back to {dynamic_app_name}, {operator_name}. All systems are online and ready."
+                # STATE 3: The Veteran (Config workflow is finalized)
+                # Even here the narration stays scripted. The model name is a
+                # receipt, so it is spoken only about the engine, never as a
+                # claim about the speaker.
+                msg = (
+                    f"Welcome back to {dynamic_app_name}, {operator_name}. This narration is still "
+                    f"scripted speech, not inference. Your local engine is {active_model}. Anything "
+                    "a model actually generates from here on will name the model that generated it."
+                )
                 return "Dashboard Ready ✅", msg, None
                 
         elif step_id == 'finalize':
(nix) pipulate $ m
📝 Committing: chore: Update introduction persona script for clarity and onboarding flow 
[main 4f52dfe3] chore: Update introduction persona script for clarity and onboarding flow
 1 file changed, 28 insertions(+), 11 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/010_introduction.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/010_introduction.py'.
(nix) pipulate $ d
diff --git a/apps/010_introduction.py b/apps/010_introduction.py
index f098ade2..002fd28c 100644
--- a/apps/010_introduction.py
+++ b/apps/010_introduction.py
@@ -14,6 +14,7 @@ Features:
 
 import time
 import os
+import re
 import json
 import asyncio
 from fasthtml.common import *
@@ -149,6 +150,13 @@ class IntroductionPlugin:
         else:
             text = str(content)
 
+        # THE NARRATOR READS PROSE, NOT MARKUP. to_xml() renders a full tag
+        # (<strong class="platform-shortcut">Ctrl+Alt+D</strong>) and Piper
+        # will happily pronounce the attributes. Strip tags and collapse the
+        # whitespace so the spoken line matches the line on the screen.
+        text = re.sub(r'<[^>]+>', ' ', text)
+        text = re.sub(r'\s+', ' ', text).strip()
+
         
         from imports.voice_synthesis import chip_voice_system
         if chip_voice_system and chip_voice_system.voice_ready:
(nix) pipulate $ m
📝 Committing: chore: Strip markup from introduction prose for voice synthesis
[main 79038d05] chore: Strip markup from introduction prose for voice synthesis
 1 file changed, 8 insertions(+)
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix) pipulate $ d
diff --git a/foo_files.py b/foo_files.py
index 9594a195..39ddca5b 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -266,6 +266,32 @@ AI_PHOOEY_CHOP = r"""
 # on its first flight; the only stale state it can ever exhibit is a loud
 # GENERATION FAILED banner, which is a wound, not a lie.
 
+# THE ATTRIBUTED-VOICE RULE (banked 2026-07-26, articulation-banked): a
+# synthesized voice is not evidence of a mind. Piper TTS reading a hardcoded
+# string is a PLAYER PIANO -- deterministic, scripted, and honest only when it
+# says so. An LLM generating a response is a MODEL, and the model gets NAMED.
+# Conviction: the first sixty seconds of onboarding had the Piper narrator
+# announce "I am Chip O'Theseus" and offer "a demonstration of my
+# capabilities," where the demonstration was a Ghost Driver JSON scenario --
+# two claims of intelligence, zero inference, in the opening minute of a
+# project whose whole thesis is that you can TELL the deterministic parts from
+# the generative ones.
+# THREE CHANNELS, NEVER BLURRED:
+#   NARRATION -- scripted TTS. Discloses that it is scripted the first time it
+#                speaks; never says "I am <persona>", never claims capability.
+#   MODEL     -- inference. Names the engine that produced it ("routed to
+#                gemma3", "Model Used: gemini-2.5-pro"), because the name is a
+#                RECEIPT, not a costume. onboard_sauce.py already complies.
+#   ACTUATOR  -- deterministic automation (Ghost Driver, scenarios, wand
+#                spells). A demonstration OF THE SYSTEM, never of "my
+#                capabilities."
+# CHIP O'THESEUS IS EARNED, NOT ASSERTED: the name belongs to the persistent
+# local model plus its keychain memory, and may be spoken only once a model is
+# actually answering. Sibling of GENERATED-NOT-AUTHORED (a capability claim no
+# live component backs is drift-by-construction) and of ANCESTOR-DISCIPLINE
+# (name the thing accurately; stage the reveal instead of leading with the
+# label that oversells).
+
 # THE SURFACE-FIRST ROUTING RULE (banked 2026-07-22, blind-test witnessed):
 # Pipulate has two Golden Paths because it has two execution surfaces.
 # Prompt Fu is the compile-time Unix-pipe/cartridge lane; FastHTML/MCP is the
(nix) pipulate $ m
📝 Committing: chore: Update documentation - attributed voice rule clarification
[main 3376c105] chore: Update documentation - attributed voice rule clarification
 1 file changed, 26 insertions(+)
(nix) pipulate $ rm -f data/.has_greeted
(nix) pipulate $
```

**4: Ignition**: Performed above

**5: Prompt**: 

Okay, I go look at the app. Nicely done! Oh, this reminds me. In both the
Config app that I showed here as part of onboarding and also in the Hello World
app there's this concept of "Name confirmed" but there's no real actual
confirmation of the name going on in the background like it suggests. That's yet
another place I need this honesty scrub. If it's not doing something like that
for real, we don't suggest it. It's merely name acquired or recorded or
something to that effect. Oh, and did I screw up by applying patch 2? Things
seem to be working.

Cars applied, ignition fired, receipts above. Two follow-ups now that the front door is honest.

First: audit the surfaces that just entered context for the same failure. `imports/voice_synthesis.py` is the boundary every scripted line crosses — decide whether the disclosure belongs there as a one-time per-session preamble rather than being re-authored in each caller, and say plainly which is better and why. `assets/scenarios/introduction.json` drives the Ctrl+Alt+D tour that the Welcome slide now calls "a scripted tour of the interface" — check whether the scenario's own narration keeps that promise or quietly re-asserts a persona.

Second: `apps/015_config.py` speaks roughly a dozen scripted lines during the four config steps. Probe 2's receipt is the full list. Grade each one against THE ATTRIBUTED-VOICE RULE's three channels and hand me only the ones that fail, as a single patch car. Do not touch the ones that already pass; "cognitive engine" is accurate when it names the engine being selected.

Then tell me whether the rule as banked is actually load-bearing or whether it is restating GENERATED-NOT-AUTHORED with a microphone. If it is redundant, say so and propose the deletion instead of defending it.

**Opus 5 High**: Context verified.

## Did you screw up by applying patch 2? No.

Probe 3 is unambiguous — `to_xml(Strong(...))` returned `<strong class="platform-shortcut">Ctrl+Alt+D</strong>`, so the tag *was* reaching the narrator and the patch was gated correctly.

But it did surface something I should have caught before writing it: **`imports/voice_synthesis.py` already strips HTML tags.** `synthesize_and_play` has an "ACOUSTIC SANITIZER" doing `re.sub(r'<[^>]+>', '', spoken_text)`. So there are now two markup strippers in the path, and the older one is the worse of the pair — it substitutes empty string instead of a space, so `press<strong>X</strong>now` becomes `pressXnow`. Nothing is broken today (your text has spaces around the tags either way), but two sanitizers means the next person fixes the wrong one. Car C below collapses them onto the better implementation at the boundary and removes the duplicate from the caller.

That duplication is also the cleanest evidence for your first question.

## Disclosure at the boundary, or at the caller?

**Caller. Not the boundary.** Three reasons, in descending order of how badly the boundary version fails:

1. **`voice_synthesis.py` cannot know what it is holding.** It receives a string. It has no way to distinguish a hardcoded literal from `wand.prompt()` output that someone later decides to read aloud — and reading model output aloud is an obvious near-term feature. A boundary-level preamble saying "this is a fixed script" would then be *lying about generated text*, which is the exact failure we just fixed, running backwards. Disclosure is a claim about provenance; only the caller knows provenance.

2. **"Session" is undefined at that layer.** `chip_voice_system` is a module-level singleton in a long-running server that this project restarts constantly — `Ctrl+Alt+R`, the watchdog on save, `pu`, the demo's own `/switch_environment` restart. First-utterance-per-process is not first-utterance-per-human, so a boundary preamble would re-fire at arbitrary moments mid-workflow. Your own `data/.has_greeted` sentinel is the correct shape for this (durable, on disk, app-layer) and it already lives at the caller.

3. **The split is already right, just unlabeled.** Sanitization is *mechanism*: content-blind, universal, safe to centralize — belongs at the boundary. Disclosure is *content*: requires knowing which channel produced the string — belongs at the caller. Car C makes that split explicit rather than accidental.

## `introduction.json`: the tour does not keep the promise

Two hard failures, both in the first fifteen seconds of the `Ctrl+Alt+D` path:

- **`03_yes_response.ollama_messages.available`** — "Hi, I'm Chip O'Theseus the local LLM built into {app_name}." This is worse than the line you just fixed. It is a hardcoded JSON string asserting that it *is* the LLM, and it fires specifically when Ollama is present, which makes the lie maximally plausible and zero-percent true. No inference has occurred at that point.
- **`04_voice_greeting`** — "I'm Chip O'Theseus, your local AI assistant, and I'm delighted to speak with you today!" Identity claim plus affect claim, routed through `voice_synthesis` as an MCP tool call.
- **`not_available`** variant — softer, but still says "me speaking out loud to you." Patched in the same block.

`07_first_trick`'s "For my first trick I will Reset Entire DEV Database" I am deliberately leaving alone. That is ACTUATOR framing and the system genuinely does the thing. Showmanship about a real deterministic act is fine; the rule targets claims of mind, not theatre.

**Separate rot finding, not patched:** the `branch_llm_body_*` and `branch_dom_visualizer_intro` branches instruct the user to type `[local_llm_read_file file_path="browser_automation/looking_at/simple_dom.html"]`. `local_llm_read_file` is **not in this compile's Tool Roster** (21 tools, AST-derived), and `browser_automation/` is not the current cache path — `scraper_tools.py` and `crawl.py` both write to `browser_cache/`. That is THE MODEL FOLLOWS THE MAP failure with a human in the seat: the demo is teaching a stranger to invoke a tool that does not exist. It needs its own turn and its own probe, so it gets probe 5 rather than a blind patch.

## `015_config.py` grading

Correction first, because it matters more than the grade: **`040_hello_workflow.py` already passes.** Its `step_01_submit` speaks `"Name saved. Hello {user_val}."` and shows `Name saved: {user_val}`. The confirmation language you remembered is only in `015_config.py`. Worth knowing you have one offender, not two.

Also worth flagging: probe 2's receipt is **truncated** — `| head -40` was consumed by `onboard_sauce.py`'s 30-odd hits, so only three `015_config.py` lines survived into the receipt. I graded from the raw source in this payload instead, which is the stronger authority anyway. The echoed probe in section 2 is re-scoped so the AFTER reading is actually complete.

**Fails (all in car A):**

| Line | String | Why it fails |
|---|---|---|
| `step_01_submit` | "Identity confirmed" / "Operator Identity secured" | Nothing verified. Three writes happened; zero checks. |
| `step_03_submit` | "Connection secured" / "Cloud Engine secured" | The key was written to `.env` and the `llm` keychain and never once used. No request was made. |
| `step_04_submit` | "Botify token secured" ×2 | Same — stored, never tested. |
| `step_04_submit` | "Congratulations, everything is configured!" | Fires unconditionally, including when the user pressed Skip on both cloud *and* Botify. |
| `finalize` | "All features requiring AI or an API-key are active" | Same unconditional capability claim, now spoken at the moment of lock-in. |

**Passes, untouched:** "Your workflow has been recovered", "A new workflow has been initialized", "Workflow unlocked. You may now revert to any step", "Workflows are Notebooks without having to look at the code", "Local cognitive engine selected", "Cloud AI skipped. You can stay fully local", "Botify integration skipped."

**Soft, flagged not patched:** `"Please select your local cognitive engine. I recommend {recommended_model}"` — "I recommend" attributes a preference to a speaker, when the recommendation is `config.DEFAULT_PROMPT_MODEL`. You said don't touch passing lines and "cognitive engine" is accurate; I agree, and one first-person verb is not worth a car. Noting it so it is a decision rather than an oversight.

## Is the rule load-bearing, or GENERATED-NOT-AUTHORED with a microphone?

**Load-bearing — and this turn is the proof, not an argument for it.**

GENERATED-NOT-AUTHORED governs *payload sections that describe live capability*: its subject is a list, its audience is a model reading a cartridge, its remedy is "compute it from live source or fail loud." Every failure I found today is a **verb of verification with nothing behind it** — "confirmed," "secured," "configured." None is a capability list. GENERATED-NOT-AUTHORED has no hook to catch any of them; there is nothing to generate, because the honest string is shorter than the dishonest one and is known at authoring time. ATTRIBUTED-VOICE catches all five.

It also carries two things the older rule does not: the three-channel taxonomy (which is what let me sort thirty-plus `speak()` calls in one pass) and the earned-name clause.

Where the rule as banked *is* wrong is its scope. It says "a synthesized voice," but three of the five convictions above are on-screen text that no one hears. Car D widens it by one clause and adds a mechanical test — if the string names an act, some code performed that act in the same turn, or the verb changes. That test is what would have caught "Connection secured" without needing my judgment.

---

### 1. PROBES

```bash
rg -n '\bre\.' apps/010_introduction.py
rg -n 'secured|confirmed|Congratulations|everything is configured' apps/015_config.py apps/040_hello_workflow.py
rg -n "Chip O'Theseus|my capabilities|delighted|local LLM" assets/scenarios/*.json
.venv/bin/python -c "import json; json.load(open('assets/scenarios/introduction.json')); print('introduction.json parses')"
rg -n 'local_llm_read_file|browser_automation/looking_at' assets/scenarios/ apps/ tools/ | head -20
```

Probe 1 gates the `import re` removal in car C — if the only hits are the two sanitizer lines, the import is dead and goes with them; any third hit and I keep the import. Probe 2 is the honesty-verb census, re-scoped so nothing truncates it this time; the AFTER reading should show only accurate uses. Probe 3 is the scenario persona census. **Probe 4 is the one that actually protects you:** `apply.py` runs an AST check for `.py` and `nix-instantiate` for `.nix`, but has **no validator for `.json`** — a SEARCH/REPLACE that lands slightly wrong in car B produces a file that parses as text and dies silently in `fetch()`. Run it before and after; the BEFORE establishes that the file is currently clean. Probe 5 documents the dead tool-name rot for a later turn.

### 2. NEXT CONTEXT

```text
! rg -n '\bre\.' apps/010_introduction.py
! rg -n 'secured|confirmed|Congratulations|everything is configured' apps/015_config.py apps/040_hello_workflow.py
! rg -n "Chip O'Theseus|my capabilities|delighted|local LLM" assets/scenarios/*.json
! .venv/bin/python -c "import json; json.load(open('assets/scenarios/introduction.json')); print('introduction.json parses')"
! rg -n 'local_llm_read_file|browser_automation/looking_at' assets/scenarios/ apps/ tools/ | head -20

apps/010_introduction.py
apps/015_config.py
apps/040_hello_workflow.py
imports/voice_synthesis.py
assets/scenarios/introduction.json
tools/mcp_tools.py
```

`tools/mcp_tools.py` is new — it is the only place that can settle whether `local_llm_read_file` was renamed or deleted, which the dead-reference turn needs. It is large; if it blows the budget, drop it and probe 5's receipt carries the finding alone.

### 3. PATCHES

**Car A — honesty scrub on `015_config.py`.** Verbs match what the code actually did.

```text
Target: apps/015_config.py
[[[SEARCH]]]
            await wand.finalize_workflow(pipeline_id)
            await self.message_queue.add(wand, self.step_messages['finalize']['complete'], verbatim=True)
            self.wand.speak("The workflow is now locked and finalized. You can pull it up again any time with the same key. All features requiring AI or an API-key are active. Enjoy the workflows!", wait=False)
[[[DIVIDER]]]
            await wand.finalize_workflow(pipeline_id)
            await self.message_queue.add(wand, self.step_messages['finalize']['complete'], verbatim=True)
            # ATTRIBUTED VOICE: the old line claimed "all features requiring AI
            # or an API-key are active" unconditionally, including for someone
            # who pressed Skip on both cloud and Botify. Nothing here inspects
            # which steps were filled in, so the narration must not assert it.
            self.wand.speak("The workflow is now locked and finalized. You can pull it up again any time with the same key. Whatever you configured is now in effect, and anything you skipped can be added later by unlocking this workflow.", wait=False)
[[[REPLACE]]]

Target: apps/015_config.py
[[[SEARCH]]]
        # Progressive feedback with emoji
        success_msg = f'{self.ui["EMOJIS"]["SUCCESS"]} Operator Identity secured: {user_val}'
        await self.message_queue.add(self.wand, success_msg, verbatim=True)

        # Speak it into existence!
        self.wand.speak(f"Identity confirmed. Hello {user_val}. Proceed to the next step.", wait=True)
[[[DIVIDER]]]
        # Progressive feedback with emoji.
        # NO VERB OF VERIFICATION WITHOUT A VERIFICATION. Three writes happened
        # above (pipeline state, server cookie, keychain) and zero checks. The
        # honest verb is "recorded"; "confirmed" and "secured" both describe an
        # act no code in this method performs.
        success_msg = f'{self.ui["EMOJIS"]["SUCCESS"]} Name recorded: {user_val}'
        await self.message_queue.add(self.wand, success_msg, verbatim=True)

        # Speak it into existence!
        self.wand.speak(f"Name recorded. Hello {user_val}. Proceed to the next step.", wait=True)
[[[REPLACE]]]

Target: apps/015_config.py
[[[SEARCH]]]
        success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Cloud Engine secured: {selected_model}'
        await self.message_queue.add(pip, success_msg, verbatim=True)
        pip.speak(f"Cloud cognitive engine selected. Connection secured. If you are a Botify employee or Customer, please enter your Botify API key.", wait=False)
[[[DIVIDER]]]
        # "Connection secured" described a connection that was never opened:
        # the key was written to .env and the llm keychain and not used once.
        # Say what happened, and name the first moment it gets tested.
        success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Cloud engine saved: {selected_model}'
        await self.message_queue.add(pip, success_msg, verbatim=True)
        pip.speak(f"Cloud engine set to {selected_model} and the key is saved. It has not been tested yet; the first real request will confirm it. If you are a Botify employee or customer, please enter your Botify API key next.", wait=False)
[[[REPLACE]]]

Target: apps/015_config.py
[[[SEARCH]]]
            success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Botify token secured.'
            await self.message_queue.add(pip, success_msg, verbatim=True)
            pip.speak("Botify API token secured.", wait=True)
[[[DIVIDER]]]
            # Stored, not validated. Same correction as the cloud step.
            success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Botify token saved (not yet tested).'
            await self.message_queue.add(pip, success_msg, verbatim=True)
            pip.speak("Botify API token saved. It has not been tested yet.", wait=True)
[[[REPLACE]]]

Target: apps/015_config.py
[[[SEARCH]]]
        pip.speak("Congratulations, everything is configured! Click finalize.", wait=False)
[[[DIVIDER]]]
        # Fires on every path through step_04, including double-Skip, so it
        # cannot claim everything is configured. It CAN claim the steps are done.
        pip.speak("Configuration steps complete. Click finalize to lock it in.", wait=False)
[[[REPLACE]]]
```

**Car B — strip the persona from the `Ctrl+Alt+D` tour.** Run probe 4 immediately after `app`, before `m`; `apply.py` has no JSON validator.

```text
Target: assets/scenarios/introduction.json
[[[SEARCH]]]
          "ollama_messages": {
            "available": "You hit yes! Hi, I'm Chip O'Theseus the local LLM built into {app_name}. Is it okay if I speak out loud to you? **Ctrl+Alt+y** / **Ctrl+Alt+n**",
            "not_available": "You hit yes but you still need to install Ollama with Gemma 3. Would you like to proceed anyway with limited LLM assistance and me speaking out loud to you? **Ctrl+Alt+y** / **Ctrl+Alt+n**"
          }
[[[DIVIDER]]]
          "ollama_messages": {
            "available": "You hit yes. One thing about what you are about to hear: this tour is a fixed script, and the voice reading it is Piper, a speech synthesizer running on your machine. A local language model IS installed and will answer you in chat later, but it is not what narrates this. Turn the voice on? **Ctrl+Alt+y** / **Ctrl+Alt+n**",
            "not_available": "You hit yes. This tour is a fixed script read aloud by Piper, a local speech synthesizer, so it runs with or without a language model. Ollama is not installed yet, so chat replies will be limited until it is. Turn the voice on? **Ctrl+Alt+y** / **Ctrl+Alt+n**"
          }
[[[REPLACE]]]

Target: assets/scenarios/introduction.json
[[[SEARCH]]]
          "text": "Welcome to the Great and Secret AI-Readiness Show! I'm Chip O'Theseus, your local AI assistant, and I'm delighted to speak with you today! At any time you can chat to me here."
[[[DIVIDER]]]
          "text": "Welcome to the Great and Secret AI-Readiness Show. Everything in this tour is a scripted demonstration of the system, narrated by a local speech synthesizer. The chat box on the right is where a real model answers, and it will always name which model answered."
[[[REPLACE]]]
```

**Car C — one owner for markup sanitization.** Apply only if probe 1 shows `re.` used nowhere else in `010_introduction.py`; if it shows a third hit, apply the `voice_synthesis.py` half and skip the import line.

```text
Target: imports/voice_synthesis.py
[[[SEARCH]]]
        # 🛡️ THE ACOUSTIC SANITIZER: Purge visual markup before synthesis
        spoken_text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', spoken_text)  # Extract markdown link text
        spoken_text = re.sub(r'\[[^\]]+\]', '', spoken_text)  # Remove silent bracket tags
        spoken_text = re.sub(r'<[^>]+>', '', spoken_text)  # Strip HTML tags
[[[DIVIDER]]]
        # 🛡️ THE ACOUSTIC SANITIZER: Purge visual markup before synthesis.
        # THIS IS THE SINGLE OWNER. Sanitization is MECHANISM -- content-blind,
        # universal, and safe to centralize here, where every scripted line and
        # every future generated line both pass through. Callers must not
        # re-implement it; a second stripper upstream means the next fix lands
        # in the wrong one. (Disclosure is the opposite: it is CONTENT, it
        # requires knowing whether the string was authored or generated, and
        # this layer cannot know that -- so it stays at the caller.)
        # Tags substitute to a SPACE, not to nothing: `press<b>X</b>now` must
        # not become `pressXnow`. Collapse afterward so the spoken line matches
        # the line on screen.
        spoken_text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', spoken_text)  # Extract markdown link text
        spoken_text = re.sub(r'\[[^\]]+\]', '', spoken_text)  # Remove silent bracket tags
        spoken_text = re.sub(r'<[^>]+>', ' ', spoken_text)  # Strip HTML tags
        spoken_text = re.sub(r'\s+', ' ', spoken_text).strip()
[[[REPLACE]]]

Target: apps/010_introduction.py
[[[SEARCH]]]
        # THE NARRATOR READS PROSE, NOT MARKUP. to_xml() renders a full tag
        # (<strong class="platform-shortcut">Ctrl+Alt+D</strong>) and Piper
        # will happily pronounce the attributes. Strip tags and collapse the
        # whitespace so the spoken line matches the line on the screen.
        text = re.sub(r'<[^>]+>', ' ', text)
        text = re.sub(r'\s+', ' ', text).strip()
[[[DIVIDER]]]
        # Markup stripping lives at the voice boundary (the ACOUSTIC SANITIZER
        # in imports/voice_synthesis.py), which every caller crosses. Duplicated
        # here it was harmless but drift-prone, so it moved rather than doubled.
[[[REPLACE]]]

Target: apps/010_introduction.py
[[[SEARCH]]]
import time
import os
import re
import json
import asyncio
[[[DIVIDER]]]
import time
import os
import json
import asyncio
[[[REPLACE]]]
```

**Car D — widen the rule's scope.**

```text
Target: foo_files.py
[[[SEARCH]]]
# actually answering. Sibling of GENERATED-NOT-AUTHORED (a capability claim no
# live component backs is drift-by-construction) and of ANCESTOR-DISCIPLINE
# (name the thing accurately; stage the reveal instead of leading with the
# label that oversells).
[[[DIVIDER]]]
# actually answering. Sibling of GENERATED-NOT-AUTHORED (a capability claim no
# live component backs is drift-by-construction) and of ANCESTOR-DISCIPLINE
# (name the thing accurately; stage the reveal instead of leading with the
# label that oversells).
# SCOPE AMENDMENT (2026-07-26, same-turn conviction): the rule covers every
# UTTERANCE, spoken OR on-screen -- "voice" in the title is the origin, not the
# boundary. Conviction: the worst offenders in the follow-up audit were TEXT.
# "Identity confirmed" (three writes, zero checks). "Connection secured" (the
# API key was written to disk and never once used). "Congratulations,
# everything is configured" (spoken on the path where the user skipped both
# cloud and Botify). "All features requiring AI or an API-key are active"
# (unconditional, at the moment of lock-in). None of those is a capability
# LIST, so GENERATED-NOT-AUTHORED has no hook for any of them -- which is what
# makes this rule load-bearing rather than that rule with a microphone.
# THE MECHANICAL TEST (use this instead of judgment): if a string names an ACT
# -- confirmed, secured, verified, connected, validated, active -- then some
# code must have PERFORMED that act in the same turn. If not, the verb changes
# to what actually happened: recorded, saved, stored, set. Storing a credential
# is not testing it, and the first real request is the earliest honest moment
# to claim a connection.
[[[REPLACE]]]
```

**IGNITION.** Cars A, C and D each need a different one, so run them in this order:

```bash
pu
rm -f data/.has_greeted
```

`015_config.py`, `010_introduction.py` and `voice_synthesis.py` are all read by the running server, so `pu` (or letting the watchdog take the save) is what makes cars A and C execute. `introduction.json` is fetched by the browser per demo run, so a hard reload is its ignition — no restart required, but the restart does not hurt it. `rm -f data/.has_greeted` again if you want to hear states 1 and 2 auto-speak rather than clicking the encore icon. Car D needs no ignition: `foo_files.py` is read by the next `ahc`, which is the compile that produces the AFTER receipts anyway.

### 4. PROMPT

```text
**MikeLev.in**: Four cars applied, ignition fired, receipts above. Two things left on this thread, then I want to hop off.

First, the dead-reference finding. Probe 5's receipt shows what `introduction.json` still tells a newcomer to type. `tools/mcp_tools.py` is in context now — settle whether `local_llm_read_file` was renamed, deleted, or never existed, and whether `browser_automation/looking_at/` was the old cache path before `browser_cache/`. Then decide the honest fix for the `branch_llm_body_*` and `branch_dom_visualizer_intro` branches: repoint them at tools that exist in the live 21-tool roster, or cut them. Say which and why. If you repoint them, every tool name in the patch must appear in this compile's Tool Roster — do not name one from memory.

Second, `apply.py` has an AST airlock for .py and nix-instantiate for .nix and nothing at all for .json, which is the gap probe 4 exists to cover by hand. Propose the third airlock: json.loads before the write, refuse with the parse error on failure, same shape as the two that already work. One car.

Then dismount. Verify against this compile's receipts that the onboarding honesty scrub actually landed, bank whatever graduates, name what carries forward unbanked, and seed the next ride's adhoc.txt.
```

### 5. EXTERNAL DELIVERABLES

None. Everything lands inside the repo.

**MikeLev.in**: 

**1: Probe**:

```bash
(nix) pipulate $ g

Blast Radius Check to establish Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is ahead of 'origin/main' by 3 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
(nix) pipulate $ rg -n '\bre\.' apps/010_introduction.py
rg -n 'secured|confirmed|Congratulations|everything is configured' apps/015_config.py apps/040_hello_workflow.py
rg -n "Chip O'Theseus|my capabilities|delighted|local LLM" assets/scenarios/*.json
.venv/bin/python -c "import json; json.load(open('assets/scenarios/introduction.json')); print('introduction.json parses')"
rg -n 'local_llm_read_file|browser_automation/looking_at' assets/scenarios/ apps/ tools/ | head -20
157:        text = re.sub(r'<[^>]+>', ' ', text)
158:        text = re.sub(r'\s+', ' ', text).strip()
apps/015_config.py
678:            logger.info(f"🧠 Operator identity '{user_val}' secured in the Subconscious.")
683:        success_msg = f'{self.ui["EMOJIS"]["SUCCESS"]} Operator Identity secured: {user_val}'
687:        self.wand.speak(f"Identity confirmed. Hello {user_val}. Proceed to the next step.", wait=True)
1071:        success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Cloud Engine secured: {selected_model}'
1073:        pip.speak(f"Cloud cognitive engine selected. Connection secured. If you are a Botify employee or Customer, please enter your Botify API key.", wait=False)
1204:            success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Botify token secured.'
1206:            pip.speak("Botify API token secured.", wait=True)
1208:        pip.speak("Congratulations, everything is configured! Click finalize.", wait=False)
assets/scenarios/introduction.json
54:            "available": "You hit yes! Hi, I'm Chip O'Theseus the local LLM built into {app_name}. Is it okay if I speak out loud to you? **Ctrl+Alt+y** / **Ctrl+Alt+n**",
78:            "text": "Welcome to the Great and Secret AI-Readiness Show! I'm Chip O'Theseus, your local AI assistant, and I'm delighted to speak with you today! At any time you can chat to me here."
83:          "description": "🎤 Chip O'Theseus speaks his first words"
153:            "not_available": "Thank you. You can always restart the demo with Ctrl+Alt+D at any time. Visit ollama.com for local LLM installation instructions."

assets/scenarios/hello_workflow_test.json
62:          "text": "Now, I will fill in the name field with Chip O'Theseus."
88:        "value": "Chip O'Theseus",
introduction.json parses
assets/scenarios/introduction.json:219:          "message": "🎭 **SUCCESS! You now have a body!**\n\n👁️ **You can see the web!**\n\n🔍 **Now investigate what you captured:**\n\n📁 **Try: [local_llm_read_file file_path=\"browser_automation/looking_at/simple_dom.html\"]**\n\n🎯 **This will show you what your 'eyes' captured!**",
assets/scenarios/introduction.json:227:          "expected_input": "[local_llm_read_file file_path=\"browser_automation/looking_at/simple_dom.html\"]",
tools/mcp_tools.py:947:async def local_llm_read_file(params: dict) -> dict:
tools/mcp_tools.py:3250:                "_local_llm_read_file"
tools/mcp_tools.py:3296:                    "3. local_llm_read_file - Examine captured content",
tools/mcp_tools.py:3302:                    "local_llm_read_file": {"file_path": "browser_cache/looking_at/simple_dom.html"},
tools/mcp_tools.py:3330:                "tools_used": ["browser_scrape_page", "local_llm_read_file"],
tools/mcp_tools.py:4271:        'local_llm_read_file',
(nix) pipulate $ 
```

**2: Context**:

```text
# adhoc.txt    _   _   _ to set context____ _   _  ___  ____  _   <F5> Simpson Couch Gag Here (explain anything to the audience you feel needs it explained)
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  Making intro of Chip O'Theseus honest.
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  Wow, Opus 5 High can do ambitious patches!
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  
# Ad Hoc CHOP: The Not-Managed-by-Git Safe-for-Client-Data place  
                                                                  
# The following 3 files ARE the system
# ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs  # <-- The "Rolling Pin" that gives the 40K foot book-spine view of book-ore.
prompt_foo.py               # <-- Prompt Fu compiler, makes the very README for AGENTS-like payload you're reading right now, but it needs to be more like that
foo_files.py                # <-- This is the router, evolving book outline and the things you pin-up to produced the recursive self-improvement loops

# BIG STANDARD STUFF (Optionally comment out any)
requirements.in             # <-- All known dependencies and (necessary) version pinning. WORA gotcha's exposed.
__init__.py                 # <-- Master versioning
pyproject.toml              # <-- The PyPI Packaging details
.gitattributes              # <-- Model: understand that `nbstripout` and `jupytext` are both in play. Just talk the human through .ipynb patches.
.gitignore                  # <-- Creates "negative space" for sub-rep's to share parent environment and "snap" proprietary secret features into place.
flake.nix                   # <-- Solves world's WRITE ONCE RUN ANYWHERE problem like Java never could. Also resolves the bootstrap paradox.
apply.py                    # <-- How can "Web UI" ChatBots edit your code? With this Aider-inspired Player Piano patch applier.
cli.py                      # <-- Catch-all actuator for PyPI envs, Python anchoring, MCP tool-call (plus alternatives) and **kwargs like wrapping for CLI

scripts/xp.py               # <-- Transforms host OS copy-paste buffer player-piano music into context-payload.
scripts/ai.py               # <-- How I constantly use local AI to write git commit messages with `m` alias.

init.lua                    # <-- Daily driver hot-keys that overlap with aliases in flake.nix
# release.py                  # <-- How everything ends up where it does (GitHub, PyPI, etc.)
# ~/repos/nixos/autognome.py  #  <-- Letting the AIs really understand my environment (The Brave Little Tailor punches above Their Weight Class proving the dunning-kruger effect the gate-keeper's (lower-case) lament.)
# scripts/foo_cartridge.py    # Needs description
# scripts/foo_replay.py       # Needs description
scripts/weblogin.py         # <-- Lets the user "warm up" the cache for their web logins at their leisure on a profile that persists.
scripts/crawl.py            # <-- Feel free to ask for something to be crawled and included in the next turn.
# imports/ascii_displays.py   # <-- The common between AI and Humans ASCII art language (contains 3rd player piano for Rich-colorizing ASCII art)
# imports/voice_synthesis.py  # <-- The wand can talk to you
# scripts/webclip_2_markdown.py    # <-- Lets you copy HTML from a browser and paste it elsewhere as Markdown (good for capturing AI thinking steps / need to shorten the name)
# scripts/release/version_sync.py  # <-- Needs to be wrapped into release.py and eliminated, I think.

#                         --- Under this line is were you paste what the AI gives you ---
#                         --- We call it context but it's really just the right-hand  ---
#                         --- blast-radius of the "probes" to make this all science.  ---

# server.py
# scripts/mcp_menu.py

# scripts/connectors/README.md
# scripts/connectors/gmail.py
# scripts/connectors/confluence.py
# scripts/connectors/jira.py
# scripts/connectors/slack.py
# scripts/connectors/botify.py
# scripts/connectors/gsc.py
# scripts/connectors/sheets.py
# scripts/connectors/wallet.py
# scripts/connectors/mcp.py

# nixops.sh                                   # <-- You've heard of GitOPs? Well, this is NixOPs. 
# remotes/honeybot/hooks/post-receive         # <-- Ever hear of GitHub Pages? Or github.io? This is that.
# remotes/honeybot/nixos/configuration.nix    # <-- It's as if Pipulate had kids. Spy kids.
# remotes/honeybot/scripts/stream.py          # <-- Starts the TV Channel streaming to YouTube-live via OBS from Nginx Honeybot XFCE Desktop. Clear?
# remotes/honeybot/scripts/score.py           # <-- Where "Greetings Entity" slideshow reads on post-receive interrupts
# remotes/honeybot/scripts/card.py            # <-- Just added for station identification breaks
# remotes/honeybot/scripts/forest.py          # <-- Likewise, just added for the new storytelling system on Honeybot
# remotes/honeybot/scripts/test_forest.py     # <-- Test Honeybot station identification sequence on Pipulate Prime
# remotes/honeybot/scripts/logs.py            # <-- The TV Show is mostly Nginx `access.log` files tailed and piped through Python to colorize (this).
# remotes/honeybot/scripts/content_loader.py  # <-- Tricky TV programming & scheduling stuff. Absolute versus relative timing. Loops. Interrupts.
# remotes/honeybot/scripts/db.py              # <-- But you can't keep your weblogs forever! And we want trending. And data-mining. Here's how.
# imports/voice_synthesis.py                  # <-- The wand can talk to you (not sure if I'm keeping it in Honeybot chapter)

! rg -n '\bre\.' apps/010_introduction.py
! rg -n 'secured|confirmed|Congratulations|everything is configured' apps/015_config.py apps/040_hello_workflow.py
! rg -n "Chip O'Theseus|my capabilities|delighted|local LLM" assets/scenarios/*.json
! .venv/bin/python -c "import json; json.load(open('assets/scenarios/introduction.json')); print('introduction.json parses')"
! rg -n 'local_llm_read_file|browser_automation/looking_at' assets/scenarios/ apps/ tools/ | head -20

apps/010_introduction.py
apps/015_config.py
apps/040_hello_workflow.py
imports/voice_synthesis.py
assets/scenarios/introduction.json
tools/mcp_tools.py
```

**3: Patches**: [patch, app, d, m, patch, app, d, m...]

```diff
(nix) pipulate $ ahe
(nix) pipulate $ g

Blast Radius Check to establish Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is ahead of 'origin/main' by 3 commits.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/015_config.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/015_config.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/015_config.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/015_config.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/015_config.py'.
(nix) pipulate $ d
diff --git a/apps/015_config.py b/apps/015_config.py
index 682ea190..cffea728 100644
--- a/apps/015_config.py
+++ b/apps/015_config.py
@@ -469,7 +469,11 @@ You're here to make the workflow concepts accessible and help users understand t
         else:
             await wand.finalize_workflow(pipeline_id)
             await self.message_queue.add(wand, self.step_messages['finalize']['complete'], verbatim=True)
-            self.wand.speak("The workflow is now locked and finalized. You can pull it up again any time with the same key. All features requiring AI or an API-key are active. Enjoy the workflows!", wait=False)
+            # ATTRIBUTED VOICE: the old line claimed "all features requiring AI
+            # or an API-key are active" unconditionally, including for someone
+            # who pressed Skip on both cloud and Botify. Nothing here inspects
+            # which steps were filled in, so the narration must not assert it.
+            self.wand.speak("The workflow is now locked and finalized. You can pull it up again any time with the same key. Whatever you configured is now in effect, and anything you skipped can be added later by unlocking this workflow.", wait=False)
             return wand.run_all_cells(app_name, steps)
 
     async def unfinalize(self, request):
@@ -679,12 +683,16 @@ You're here to make the workflow concepts accessible and help users understand t
         except ImportError as e:
             logger.warning(f"⚠️ Could not access AI Keychain to store operator identity: {e}")
 
-        # Progressive feedback with emoji
-        success_msg = f'{self.ui["EMOJIS"]["SUCCESS"]} Operator Identity secured: {user_val}'
+        # Progressive feedback with emoji.
+        # NO VERB OF VERIFICATION WITHOUT A VERIFICATION. Three writes happened
+        # above (pipeline state, server cookie, keychain) and zero checks. The
+        # honest verb is "recorded"; "confirmed" and "secured" both describe an
+        # act no code in this method performs.
+        success_msg = f'{self.ui["EMOJIS"]["SUCCESS"]} Name recorded: {user_val}'
         await self.message_queue.add(self.wand, success_msg, verbatim=True)
 
         # Speak it into existence!
-        self.wand.speak(f"Identity confirmed. Hello {user_val}. Proceed to the next step.", wait=True)
+        self.wand.speak(f"Name recorded. Hello {user_val}. Proceed to the next step.", wait=True)
 
         # Update LLM context
         self.wand.append_to_history(f"[SYSTEM STATE] Operator identity established globally as:\n{user_val}")
@@ -1068,9 +1076,12 @@ You're here to make the workflow concepts accessible and help users understand t
         pip.append_to_history(f"[WIDGET CONTENT] {step.show}:\n{display_text}")
         pip.append_to_history(f"[WIDGET STATE] {step.show}: Step completed")
         
-        success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Cloud Engine secured: {selected_model}'
+        # "Connection secured" described a connection that was never opened:
+        # the key was written to .env and the llm keychain and not used once.
+        # Say what happened, and name the first moment it gets tested.
+        success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Cloud engine saved: {selected_model}'
         await self.message_queue.add(pip, success_msg, verbatim=True)
-        pip.speak(f"Cloud cognitive engine selected. Connection secured. If you are a Botify employee or Customer, please enter your Botify API key.", wait=False)
+        pip.speak(f"Cloud engine set to {selected_model} and the key is saved. It has not been tested yet; the first real request will confirm it. If you are a Botify employee or customer, please enter your Botify API key next.", wait=False)
         
         if pip.check_finalize_needed(step_index, steps):
             await self.message_queue.add(pip, self.step_messages['finalize']['ready'], verbatim=True)
@@ -1201,11 +1212,14 @@ You're here to make the workflow concepts accessible and help users understand t
             }
             display_text = f"Status: Configured\nToken: {masked_key}"
             
-            success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Botify token secured.'
+            # Stored, not validated. Same correction as the cloud step.
+            success_msg = f'{pip.get_ui_constants()["EMOJIS"]["SUCCESS"]} Botify token saved (not yet tested).'
             await self.message_queue.add(pip, success_msg, verbatim=True)
-            pip.speak("Botify API token secured.", wait=True)
+            pip.speak("Botify API token saved. It has not been tested yet.", wait=True)
 
-        pip.speak("Congratulations, everything is configured! Click finalize.", wait=False)
+        # Fires on every path through step_04, including double-Skip, so it
+        # cannot claim everything is configured. It CAN claim the steps are done.
+        pip.speak("Configuration steps complete. Click finalize to lock it in.", wait=False)
             
         await pip.set_step_data(pipeline_id, step_id, payload, steps)
         
(nix) pipulate $ m
📝 Committing: refactor: improve workflow narration and remove redundant assertions in operator identity steps
[main 696d2478] refactor: improve workflow narration and remove redundant assertions in operator identity steps
 1 file changed, 23 insertions(+), 9 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'assets/scenarios/introduction.json'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'assets/scenarios/introduction.json'.
(nix) pipulate $ d
diff --git a/assets/scenarios/introduction.json b/assets/scenarios/introduction.json
index 63e8ef8a..da0f36ef 100644
--- a/assets/scenarios/introduction.json
+++ b/assets/scenarios/introduction.json
@@ -51,8 +51,8 @@
           },
           "ollama_check": true,
           "ollama_messages": {
-            "available": "You hit yes! Hi, I'm Chip O'Theseus the local LLM built into {app_name}. Is it okay if I speak out loud to you? **Ctrl+Alt+y** / **Ctrl+Alt+n**",
-            "not_available": "You hit yes but you still need to install Ollama with Gemma 3. Would you like to proceed anyway with limited LLM assistance and me speaking out loud to you? **Ctrl+Alt+y** / **Ctrl+Alt+n**"
+            "available": "You hit yes. One thing about what you are about to hear: this tour is a fixed script, and the voice reading it is Piper, a speech synthesizer running on your machine. A local language model IS installed and will answer you in chat later, but it is not what narrates this. Turn the voice on? **Ctrl+Alt+y** / **Ctrl+Alt+n**",
+            "not_available": "You hit yes. This tour is a fixed script read aloud by Piper, a local speech synthesizer, so it runs with or without a language model. Ollama is not installed yet, so chat replies will be limited until it is. Turn the voice on? **Ctrl+Alt+y** / **Ctrl+Alt+n**"
           }
         }
       ],
@@ -75,7 +75,7 @@
           "type": "mcp_tool_call",
           "tool_name": "voice_synthesis",
           "tool_args": {
-            "text": "Welcome to the Great and Secret AI-Readiness Show! I'm Chip O'Theseus, your local AI assistant, and I'm delighted to speak with you today! At any time you can chat to me here."
+            "text": "Welcome to the Great and Secret AI-Readiness Show. Everything in this tour is a scripted demonstration of the system, narrated by a local speech synthesizer. The chat box on the right is where a real model answers, and it will always name which model answered."
           },
           "timing": {
             "delay_before": 300
(nix) pipulate $ m
📝 Committing: chore: Update introduction scenario text for voice synthesis clarity
[main bfc57431] chore: Update introduction scenario text for voice synthesis clarity
 1 file changed, 3 insertions(+), 3 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'imports/voice_synthesis.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/010_introduction.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apps/010_introduction.py'.
(nix) pipulate $ d
diff --git a/apps/010_introduction.py b/apps/010_introduction.py
index 002fd28c..5227e552 100644
--- a/apps/010_introduction.py
+++ b/apps/010_introduction.py
@@ -14,7 +14,6 @@ Features:
 
 import time
 import os
-import re
 import json
 import asyncio
 from fasthtml.common import *
@@ -150,12 +149,9 @@ class IntroductionPlugin:
         else:
             text = str(content)
 
-        # THE NARRATOR READS PROSE, NOT MARKUP. to_xml() renders a full tag
-        # (<strong class="platform-shortcut">Ctrl+Alt+D</strong>) and Piper
-        # will happily pronounce the attributes. Strip tags and collapse the
-        # whitespace so the spoken line matches the line on the screen.
-        text = re.sub(r'<[^>]+>', ' ', text)
-        text = re.sub(r'\s+', ' ', text).strip()
+        # Markup stripping lives at the voice boundary (the ACOUSTIC SANITIZER
+        # in imports/voice_synthesis.py), which every caller crosses. Duplicated
+        # here it was harmless but drift-prone, so it moved rather than doubled.
 
         
         from imports.voice_synthesis import chip_voice_system
diff --git a/imports/voice_synthesis.py b/imports/voice_synthesis.py
index 7b34a380..4023f169 100644
--- a/imports/voice_synthesis.py
+++ b/imports/voice_synthesis.py
@@ -143,10 +143,21 @@ class ChipVoiceSystem:
         spoken_text = re.sub(r'\*?\*?MikeLev\.in\*?\*?:?', 'Mike:', text, flags=re.IGNORECASE)
         # ---------------------------------------------------------
 
-        # 🛡️ THE ACOUSTIC SANITIZER: Purge visual markup before synthesis
+        # 🛡️ THE ACOUSTIC SANITIZER: Purge visual markup before synthesis.
+        # THIS IS THE SINGLE OWNER. Sanitization is MECHANISM -- content-blind,
+        # universal, and safe to centralize here, where every scripted line and
+        # every future generated line both pass through. Callers must not
+        # re-implement it; a second stripper upstream means the next fix lands
+        # in the wrong one. (Disclosure is the opposite: it is CONTENT, it
+        # requires knowing whether the string was authored or generated, and
+        # this layer cannot know that -- so it stays at the caller.)
+        # Tags substitute to a SPACE, not to nothing: `press<b>X</b>now` must
+        # not become `pressXnow`. Collapse afterward so the spoken line matches
+        # the line on screen.
         spoken_text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', spoken_text)  # Extract markdown link text
         spoken_text = re.sub(r'\[[^\]]+\]', '', spoken_text)  # Remove silent bracket tags
-        spoken_text = re.sub(r'<[^>]+>', '', spoken_text)  # Strip HTML tags
+        spoken_text = re.sub(r'<[^>]+>', ' ', spoken_text)  # Strip HTML tags
+        spoken_text = re.sub(r'\s+', ' ', spoken_text).strip()
         
         try:
             # Serialize speech across processes (installer vs. server vs. wand)
(nix) pipulate $ m
📝 Committing: refactor: Move markup stripping to voice_synthesis.py # Remove duplicated logic
[main 4030609c] refactor: Move markup stripping to voice_synthesis.py # Remove duplicated logic
 2 files changed, 16 insertions(+), 9 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix) pipulate $ d
diff --git a/foo_files.py b/foo_files.py
index 39ddca5b..79861ade 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -291,6 +291,22 @@ AI_PHOOEY_CHOP = r"""
 # live component backs is drift-by-construction) and of ANCESTOR-DISCIPLINE
 # (name the thing accurately; stage the reveal instead of leading with the
 # label that oversells).
+# SCOPE AMENDMENT (2026-07-26, same-turn conviction): the rule covers every
+# UTTERANCE, spoken OR on-screen -- "voice" in the title is the origin, not the
+# boundary. Conviction: the worst offenders in the follow-up audit were TEXT.
+# "Identity confirmed" (three writes, zero checks). "Connection secured" (the
+# API key was written to disk and never once used). "Congratulations,
+# everything is configured" (spoken on the path where the user skipped both
+# cloud and Botify). "All features requiring AI or an API-key are active"
+# (unconditional, at the moment of lock-in). None of those is a capability
+# LIST, so GENERATED-NOT-AUTHORED has no hook for any of them -- which is what
+# makes this rule load-bearing rather than that rule with a microphone.
+# THE MECHANICAL TEST (use this instead of judgment): if a string names an ACT
+# -- confirmed, secured, verified, connected, validated, active -- then some
+# code must have PERFORMED that act in the same turn. If not, the verb changes
+# to what actually happened: recorded, saved, stored, set. Storing a credential
+# is not testing it, and the first real request is the earliest honest moment
+# to claim a connection.
 
 # THE SURFACE-FIRST ROUTING RULE (banked 2026-07-22, blind-test witnessed):
 # Pipulate has two Golden Paths because it has two execution surfaces.
(nix) pipulate $ m
📝 Committing: chore: Amend rule regarding utterance verification details
[main 191b76d2] chore: Amend rule regarding utterance verification details
 1 file changed, 16 insertions(+)
(nix) pipulate $ git push
Enumerating objects: 39, done.
Counting objects: 100% (39/39), done.
Delta compression using up to 48 threads
Compressing objects: 100% (29/29), done.
Writing objects: 100% (29/29), 7.04 KiB | 1.41 MiB/s, done.
Total 29 (delta 22), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (22/22), completed with 10 local objects.
To github.com:pipulate/pipulate.git
   12bb54f1..191b76d2  main -> main
(nix) pipulate $ 
```

**4: Ignition**: [make the patched code RUN, then re-run the Probe verbatim]

Okay `pu` was run for the server start-up. It started up and I `Ctrl+C`d out of
it and did this:

```bash
(nix) pipulate $ rm -f data/.has_greeted
(nix) pipulate $
```

And then I restarted the server the normal `python server.py` way because I
usually always keep it running and the `pu` trigger interferes with that, but
that's fine. I'm just documenting that fact here to eliminate ambiguity about
whether server.py is running when `ahc` is run. It is.

**5: Prompt**: [The AI prompting itself (BEWARB the jabberwocky ouroboros dinosaurs!]

Four cars applied, ignition fired, receipts above. Two things left on this thread, then I want to hop off.

First, the dead-reference finding. Probe 5's receipt shows what `introduction.json` still tells a newcomer to type. `tools/mcp_tools.py` is in context now — settle whether `local_llm_read_file` was renamed, deleted, or never existed, and whether `browser_automation/looking_at/` was the old cache path before `browser_cache/`. Then decide the honest fix for the `branch_llm_body_*` and `branch_dom_visualizer_intro` branches: repoint them at tools that exist in the live 21-tool roster, or cut them. Say which and why. If you repoint them, every tool name in the patch must appear in this compile's Tool Roster — do not name one from memory.

Second, `apply.py` has an AST airlock for .py and nix-instantiate for .nix and nothing at all for .json, which is the gap probe 4 exists to cover by hand. Propose the third airlock: json.loads before the write, refuse with the parse error on failure, same shape as the two that already work. One car.

Then dismount. Verify against this compile's receipts that the onboarding honesty scrub actually landed, bank whatever graduates, name what carries forward unbanked, and seed the next ride's adhoc.txt.

Yeah, I like this. This will be a short article with a sharp point on it if this
wraps up here. Let's do the dismount if we can.

Hop off the worm. This ride's stated goal is reached — dismount. Answer all four
beats, briefly:

1. VERIFY: restate the goal from the top of this article and confirm
   (or deny) it was met, citing THIS compile's receipts, not memory.
   Name any ignition this ride required that never fired -- an AFTER
   tap taken without one is a stale BEFORE wearing the AFTER's label.
2. BANK: name everything that graduates — rule, earmark, todo, pin —
   as exact paste-ready lines, plus the exact lines to delete.
3. DANGLING: what carries forward unbanked? One line each, no essays.
4. SEED: end with the adhoc.txt lines (and TODO_SLUGS if narrative
   context is needed) for the next ride's first compile.

Oh, and as we wind down we also have to think about those transitions of my
Sci-Fi oriented vocabulary here for maximum high-signal and how we're going to
translate that to the common lexicon which luckily is springing up around this
Markdown stuff I've been doing for a decade so these things should just click
together with an easy Glossary addition to the Future-proofing AI Book.

**Opus 5 High**: Context verified.

## Two open items, settled

**The dead reference.** `local_llm_read_file` was **neither renamed nor deleted — it was orphaned.** Receipt 5 shows the def alive at `tools/mcp_tools.py:947`, but it carries no `@auto_tool` decorator, so it is absent from this compile's AST-derived Tool Roster (21 tools). The same receipt shows `mcp_tools.py:3302` already writing `browser_cache/looking_at/simple_dom.html` while `introduction.json:219,227` still teach `browser_automation/looking_at/` — so the cache path *was* migrated and the scenario was left behind. Receipt 5 also convicts a second thing you didn't ask about: `mcp_tools.py:4271` is a hand-authored `public_tool_names` list inside `get_available_tools()` that names the orphan. That is GENERATED-NOT-AUTHORED with a different hat on.

**The decision: cut, don't repoint.** Three failures are stacked in those branches, not one. The path is stale (fixable). The tool is unregistered (fixable). But `"I'm going to give you a body!"` and `"You now have a body!"` are persona-and-capability claims of exactly the kind this whole ride removed — and repointing them at `browser_scrape_page` would leave a stranger, four minutes into their first session, being told to hand-type MCP bracket syntax that must exactly match an `expected_input` string. Repointing polishes an instruction that shouldn't be given. The demo's honest ending already exists at `branch_dev_reset_no`'s "the show must go on."

I am **not** patching that this turn, for two reasons. First, I can name tools from the roster but I cannot verify their `params` shapes from this context, and a scenario patch that names a real tool with wrong arguments is worse than one naming a fake tool. Second — and this is the ordering that makes the next ride clean — **the airlock should exist before the surgery that needs it.** Receipt 4 is the only thing standing between you and a silently corrupted `introduction.json` right now, and it's a hand-run.

**The airlock** is Car E below. `json.loads` before the write, refuse with the parse error, same shape as the two that already work, in both the surgical path and the WRITE_FILE path.

---

## 1. VERIFY

**The goal, restated from the top of the article:** the first sixty seconds claimed *"I am Chip O'Theseus"* and offered *"a demonstration of my capabilities"* when it was Piper reading a script — make it literal about what's going on, smooth the onboarding around it, and constitutionalize the distinction if it survives contact.

**Met.** This compile's receipts, not memory:

- **Receipt 3** — the persona census over `assets/scenarios/*.json` returns exactly four hits, and **zero are user-facing narration**: `introduction.json:83` is a step `description` (internal metadata), `:153` is an accurate Ollama install pointer, and the two `hello_workflow_test.json` hits are a sample *value* typed into a name field. The `available` branch's "I'm Chip O'Theseus the local LLM" and the voice greeting's "I'm delighted" are gone.
- **Receipt 2** — the honesty-verb census returns four hits in `015_config.py` and **three are the explanatory comments** the patch added (`:689`, `:1079`, `:1221`). The only survivor is `:682`, a `logger.info` line. Every user-facing "secured / confirmed / Congratulations" string is gone. `040_hello_workflow.py` returns nothing, confirming it never had the defect.
- **Receipt 1** — silent, exit 1. `re.` appears nowhere in `010_introduction.py`, so Car C's `import re` removal and the duplicate stripper deletion both landed and nothing else in that file depended on the import. A grep whose pass condition is no-match; the exit code is the whole receipt.
- **Receipt 4** — `introduction.json parses`, after two SEARCH/REPLACE edits into the one format `apply.py` has no validator for.
- **The source itself** — `010_introduction.py`'s three states in this payload all now open with the Piper disclosure, and `voice_synthesis.py` carries the single-owner sanitizer substituting a space and collapsing whitespace.
- **The constitution** — ATTRIBUTED-VOICE RULE and its SCOPE AMENDMENT are both visible in this compile's `foo_files.py` body.

**Probe/patch pairing note, since it matters here:** for a pure string edit, the string *is* the artifact, so an `rg` receipt is a genuine witness rather than the ACTUATION-BOUNDARY trap. Receipts 1–3 measure exactly what changed. That does not hold for Car E, which changes *behavior* — hence its canary below.

**Ignition that never fired:** `assets/scenarios/introduction.json`. Its ignition is a browser hard-reload plus an actual `Ctrl+Alt+D` run, and **no receipt in this compile witnesses the scenario playing its new lines.** The file changed and parses; nobody has heard it. Same for the `015_config.py` speech — the server restarted, but no receipt shows anyone walking the four config steps since. Both are string-only edits so the risk is low, but the AFTER tap for the *audible* result is owed and unpaid. Everything else ignited: `python server.py` after `rm -f data/.has_greeted`, and `apply.py` self-ignites per invocation.

## 2. BANK

Both constitutional additions already landed mid-ride, so nothing needs re-pasting — what graduates is the **witness stamp** (the amendment was written same-turn as its conviction and is now receipt-backed) and **one TODO** for the demo surgery. Both ride as Car F below rather than as loose paste, since `foo_files.py` is in context and exact-match beats hand-editing.

**Lines to delete: none this ride.** No pin's OWES clause emptied, no earmark graduated. Saying so explicitly rather than manufacturing a deletion.

## 3. DANGLING

- `local_llm_read_file` lives at `mcp_tools.py:947` undecorated — orphaned source, not in the 21-tool registry.
- `get_available_tools()`'s hand-authored `public_tool_names` list (`mcp_tools.py:4271`) names orphans; a roster that isn't generated.
- `015_config.py:682` still logs "secured in the Subconscious" — log lane, same untrue verb, deliberately unpatched.
- `introduction.json:83`'s `description` metadata still says "Chip O'Theseus speaks his first words"; unrendered as far as I can tell, unverified.
- `hello_workflow_test.json`'s Ghost Driver speaks first person ("Now, I will fill in…") — actuator narration, borderline under the rule, unaudited.
- `015_config.py`'s "I recommend {model}" attributes a preference to a speaker when the source is `config.DEFAULT_PROMPT_MODEL`.
- `onboard_sauce.py`'s `interrogate_local_ai` says "I can read and summarize local files instantly" — dormant, uncalled, untouched.
- The `Ctrl+Alt+D` tour post-patch has never been played by a human or a receipt.
- The sci-fi→common-lexicon glossary (below) — articulated, not yet written anywhere durable.

**On the vocabulary transition**, since you raised it at the dismount: the mapping is mostly one-to-one and the industry moved toward you, not away. Worm ride → work session. Chisel-strike → bounded probe. Probe/Patch/Prompt → instrument, change, hand-off. Cartridge → context bundle (AGENTS.md / SKILL.md class). Prompt Fu → context compiler. Amnesiac Genie → stateless session. Player piano / Ghost Driver → deterministic actuator / scripted E2E scenario. Chip O'Theseus → persistent local model plus its keychain. Rolling pin → corpus index. Blast radius → commit boundary. Straddle → before/after measurement. The glossary earmark is in Car F; the *article* that writes it is a different ride.

## 4. SEED

Next ride's shape, in order: land the airlock (Car E, this turn), then cut the demo branches behind it, then de-orphan or delete `local_llm_read_file` and replace `public_tool_names` with a generated roster.

---

### 1. PROBES

```bash
rg -n 'JSON SYNTAX AIRLOCK' apply.py
.venv/bin/python cli.py call local_llm_read_file --json-args '{"file_path":"browser_cache/looking_at/simple_dom.html"}' 2>&1 | tail -5
rg -c '@auto_tool' tools/mcp_tools.py
rg -n 'local_llm_read_file|browser_automation/looking_at' assets/scenarios/introduction.json
```

Probe 1 is the straddle for Car E: silent now, two lines after. Probe 2 is the falsifying probe for the orphan claim — if it executes, the bracket lane resolves outside the `@auto_tool` registry and my "cut it" reasoning loses one of its three legs; if it reports tool-not-found, the branches are unambiguously teaching a dead call. Probe 3 counts how much of `mcp_tools.py` is actually registered versus merely present, which sizes the de-orphaning job. Probe 4 narrows receipt 5 to the one file the next surgery touches, so the AFTER reading is a clean before/after on the cut.

### 2. NEXT CONTEXT

```text
! rg -n 'JSON SYNTAX AIRLOCK' apply.py
! .venv/bin/python cli.py call local_llm_read_file --json-args '{"file_path":"browser_cache/looking_at/simple_dom.html"}' 2>&1 | tail -5
! rg -c '@auto_tool' tools/mcp_tools.py
! rg -n 'local_llm_read_file|browser_automation/looking_at' assets/scenarios/introduction.json

apply.py
assets/scenarios/introduction.json
assets/player-piano.js
imports/stream_orchestrator.py
imports/mcp_orchestrator.py
tools/__init__.py
cli.py
```

`player-piano.js` is required before cutting branches — it is the interpreter, and I must see how it handles a branch key that no longer resolves. `stream_orchestrator.py` and `mcp_orchestrator.py` settle probe 2's mechanism regardless of which way the receipt lands. `tools/__init__.py` holds `auto_tool` and `get_all_tools`, which is where a generated roster would replace `public_tool_names`. `tools/mcp_tools.py` is deliberately **dropped** — it is 36k tokens and probes 2 and 3 carry the finding without it.

### 3. PATCHES

**Car E — the third airlock.**

```text
Target: apply.py
[[[SEARCH]]]
        # AST VALIDATION AIRLOCK (same safeguard the surgical path enforces)
        if filename.endswith('.py'):
            import ast
[[[DIVIDER]]]
        # JSON SYNTAX AIRLOCK (whole-file arm)
        if filename.endswith('.json'):
            import json
            try:
                json.loads(file_content)
            except json.JSONDecodeError as e:
                print(f"❌ Error: Whole-file write of '{filename}' aborted. Invalid JSON:\n   {e}")
                success = False
                continue

        # AST VALIDATION AIRLOCK (same safeguard the surgical path enforces)
        if filename.endswith('.py'):
            import ast
[[[REPLACE]]]

Target: apply.py
[[[SEARCH]]]
        # NIX SYNTAX AIRLOCK
        if filename.endswith('.nix'):
            import tempfile
[[[DIVIDER]]]
        # JSON SYNTAX AIRLOCK (third of three). .py gets AST, .nix gets
        # nix-instantiate, and .json got NOTHING until scenario patches
        # started landing into it -- a format where a misplaced comma
        # produces a file that still reads as text, writes without complaint,
        # and dies silently in the browser's fetch(). Conviction: the
        # 2026-07-26 introduction.json edits were proven clean only by a
        # hand-run json.load echoed as a probe, because this branch did not
        # exist. JSONDecodeError carries lineno/colno, so the refusal names
        # the exact character that broke.
        if filename.endswith('.json'):
            import json
            try:
                json.loads(new_content)
            except json.JSONDecodeError as e:
                print(f"❌ Error: Patching '{filename}' aborted. Invalid JSON:\n   {e}")
                err_lines = new_content.split('\n')
                start = max(0, e.lineno - 3)
                end = min(len(err_lines), e.lineno + 2)
                print("--- DIAGNOSTIC: Context around JSON error ---")
                for i, ln in enumerate(err_lines[start:end], start=start + 1):
                    marker = " >>>" if i == e.lineno else "    "
                    print(f"{marker} {i:4d}: {ln}")
                print("--- END DIAGNOSTIC ---")
                success = False
                continue

        # NIX SYNTAX AIRLOCK
        if filename.endswith('.nix'):
            import tempfile
[[[REPLACE]]]
```

**Car F — bank the witness stamp and the demo TODO.**

```text
Target: foo_files.py
[[[SEARCH]]]
# is not testing it, and the first real request is the earliest honest moment
# to claim a connection.
[[[DIVIDER]]]
# is not testing it, and the first real request is the earliest honest moment
# to claim a connection.
# WITNESSED 2026-07-26 (same-ride receipts, promoting both the rule and this
# amendment from articulation-banked to receipt-backed): the persona census
# over assets/scenarios/*.json returned four hits and ZERO user-facing
# narration among them; the honesty-verb census over apps/015_config.py
# returned four hits of which three were the explanatory comments the fix
# added and the fourth was a logger line. The mechanical test found five
# offenders that GENERATED-NOT-AUTHORED could not see, which is the evidence
# that this rule is load-bearing rather than that rule with a microphone.
[[[REPLACE]]]

Target: foo_files.py
[[[SEARCH]]]
# #todo #to-do
# - EARMARK: THE THREE-TIER WORKSPACE & HUMAN COMPOSITION LADDER (seeded 2026-07-25):
[[[DIVIDER]]]
# #todo #to-do
# - TODO: CUT THE GHOST-DRIVER BODY BRANCHES (seeded 2026-07-26, receipt-convicted):
#   assets/scenarios/introduction.json's branch_llm_body_chain_reaction,
#   branch_llm_body_test_results, and branch_dom_visualizer_intro stack THREE
#   failures: (1) they teach a stranger to hand-type
#   [local_llm_read_file file_path="browser_automation/looking_at/simple_dom.html"],
#   naming a function that exists at mcp_tools.py:947 but carries no @auto_tool
#   and is therefore absent from the live 21-tool roster; (2) browser_automation/
#   is the RETIRED cache path -- mcp_tools.py:3302 already writes
#   browser_cache/looking_at/; (3) "I'm going to give you a body" and "You now
#   have a body" are persona-and-capability claims of exactly the kind the
#   ATTRIBUTED-VOICE ride removed everywhere else. RULING: CUT, do not repoint.
#   Repointing polishes an instruction that should not be given at minute four
#   of a first session, and a scenario naming a REAL tool with WRONG params is
#   worse than one naming a fake tool. The honest ending already exists at
#   branch_dev_reset_no. ORDERING: the JSON airlock (apply.py) lands FIRST --
#   this is the surgery that needs it. Sibling debt: mcp_tools.py:4271's
#   hand-authored public_tool_names list names the orphan, which is
#   GENERATED-NOT-AUTHORED wearing a different hat; replace with a generated
#   roster or delete the orphan outright.
# - EARMARK: THE PROMPT-FU GLOSSARY (seeded 2026-07-26): the sci-fi lexicon is
#   nearly one-to-one with the vocabulary the agentic-web field grew on its own,
#   so the translation is a GLOSSARY, not a rewrite -- and per the
#   CODEX-AS-DATASTRUCTURE RULE a glossary is a controlled vocabulary, a type
#   system for prose that stops terms drifting between chapters. Pairs: worm
#   ride -> work session; chisel-strike -> bounded probe; Probe/Patch/Prompt ->
#   instrument, change, hand-off; cartridge (foo.zip) -> context bundle
#   (AGENTS.md/SKILL.md class); Prompt Fu -> context compiler; Amnesiac Genie ->
#   stateless session; player piano / Ghost Driver -> deterministic actuator /
#   scripted E2E scenario; Chip O'Theseus -> persistent local model + keychain;
#   rolling pin -> corpus index; paintbox -> coverage ledger; blast radius ->
#   commit boundary; straddle -> before/after measurement; Honeybot -> origin
#   telemetry lab. Keep BOTH columns: the sci-fi term is the memorable handle,
#   the common term is the searchable one, and dropping either loses a reader.
# - EARMARK: THE THREE-TIER WORKSPACE & HUMAN COMPOSITION LADDER (seeded 2026-07-25):
[[[REPLACE]]]
```

**IGNITION — none required.** `apply.py` is invoked fresh by `app` (`cat patch | python apply.py`), so the very next patch application runs the patched file; `foo_files.py` is read by the next `ahc`, which is the compile that collects the AFTER receipts anyway.

**Car E verification — manufacture a red before trusting the green.** The FAILED-PROBE RECEIPT method: a validator is trusted only once it has demonstrably refused. This mutates a scratch file in `/tmp`, never the repo, which is why it rides here as an actuator and not in PROBES:

```bash
mkdir -p /tmp/jsonairlock && cd /tmp/jsonairlock && echo '{"ok":1}' > t.json && printf 'Target: t.json\n[[[WRITE_FILE]]]\n{"broken":,}\n[[[END_WRITE_FILE]]]\n' | python "$PIPULATE_ROOT/apply.py"; cat t.json; cd "$PIPULATE_ROOT"
```

Pass condition: the airlock prints its refusal **and** `t.json` still reads `{"ok":1}`. If the file changed, the airlock is decorative and Car E must be re-cut before any scenario surgery.

### 4. PROMPT

```text
**MikeLev.in**: Airlock landed, canary confirmed red, receipts above. New ride: cut the ghost-driver body branches.

Probe 2's receipt settles whether the bracket lane resolves outside the @auto_tool registry, and `imports/stream_orchestrator.py` plus `imports/mcp_orchestrator.py` are in context to show the mechanism either way. Read them and tell me plainly what happens today when a user types `[local_llm_read_file file_path="..."]` into the chat box — does it dispatch, does it fail loudly, or does it silently do nothing. That answer decides how urgent the cut is.

## The Danger of the Silent Fallback

Then do the surgery on `assets/scenarios/introduction.json`. `assets/player-piano.js` is in context: before cutting, show me how it handles a `branches` key that no longer exists and a step whose `wait_for_input` never resolves, because a half-cut scenario that hangs on a keystroke nobody knows to press is worse than a scenario that teaches a dead tool. Cut `branch_llm_body_chain_reaction`, `branch_llm_body_test_results`, and `branch_dom_visualizer_intro`, and make `09_llm_body_test` land on an honest ending — the demo's real closer is the DB reset trick plus a plain hand-off to the chat box, where a model actually answers and names itself. One car, and run the airlock's own verdict as the receipt this time instead of a hand-run json.load.

Second car if it is small: `tools/__init__.py` is in context. Replace `mcp_tools.py`'s hand-authored `public_tool_names` list with something derived from the live registry, or tell me why deleting `get_available_tools()` outright is the better move given that `cli.py mcp-discover` already does this job properly. Do not patch `mcp_tools.py` blind — it is not in this compile.
```

### 5. EXTERNAL DELIVERABLES

None. Everything lands inside the repo.

**MikeLev.in**: 

**1: Probe**:

```bash
(nix) pipulate $ g

Blast Radius Check to establish Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(nix) pipulate $ rg -n 'JSON SYNTAX AIRLOCK' apply.py
.venv/bin/python cli.py call local_llm_read_file --json-args '{"file_path":"browser_cache/looking_at/simple_dom.html"}' 2>&1 | tail -5
rg -c '@auto_tool' tools/mcp_tools.py
rg -n 'local_llm_read_file|browser_automation/looking_at' assets/scenarios/introduction.json
Available tools: ['builtin_get_cat_fact', 'pipeline_state_inspector', 'execute_ai_session_hijacking_demonstration', 
'ui_flash_element', 'voice_synthesis', 'ui_list_elements', 'browser_scrape_page', 'visualize_dom_hierarchy', 
'visualize_dom_boxes', 'summarize_accessibility_tree', 'selenium_automation', 'system_list_directory', 'execute_shell_command',
'conversation_history_view', 'conversation_history_stats', 'conversation_history_clear', 'keychain_set', 'keychain_get', 
'keychain_delete', 'keychain_list_keys', 'keychain_get_all']
7
219:          "message": "🎭 **SUCCESS! You now have a body!**\n\n👁️ **You can see the web!**\n\n🔍 **Now investigate what you captured:**\n\n📁 **Try: [local_llm_read_file file_path=\"browser_automation/looking_at/simple_dom.html\"]**\n\n🎯 **This will show you what your 'eyes' captured!**",
227:          "expected_input": "[local_llm_read_file file_path=\"browser_automation/looking_at/simple_dom.html\"]",
(nix) pipulate $ 
```

**2: Context**:

```text
# adhoc.txt    _   _   _ to set context____ _   _  ___  ____  _   <F5> Simpson Couch Gag Here (explain anything to the audience you feel needs it explained)
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  Making intro of Chip O'Theseus honest.
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  Wow, Opus 5 High can do ambitious patches!
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  Polishing messaging.
# Ad Hoc CHOP: The Not-Managed-by-Git Safe-for-Client-Data place  
                                                                  
# The following 3 files ARE the system
# ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs  # <-- The "Rolling Pin" that gives the 40K foot book-spine view of book-ore.
prompt_foo.py               # <-- Prompt Fu compiler, makes the very README for AGENTS-like payload you're reading right now, but it needs to be more like that
foo_files.py                # <-- This is the router, evolving book outline and the things you pin-up to produced the recursive self-improvement loops

# BIG STANDARD STUFF (Optionally comment out any)
requirements.in             # <-- All known dependencies and (necessary) version pinning. WORA gotcha's exposed.
__init__.py                 # <-- Master versioning
pyproject.toml              # <-- The PyPI Packaging details
.gitattributes              # <-- Model: understand that `nbstripout` and `jupytext` are both in play. Just talk the human through .ipynb patches.
.gitignore                  # <-- Creates "negative space" for sub-rep's to share parent environment and "snap" proprietary secret features into place.
flake.nix                   # <-- Solves world's WRITE ONCE RUN ANYWHERE problem like Java never could. Also resolves the bootstrap paradox.
apply.py                    # <-- How can "Web UI" ChatBots edit your code? With this Aider-inspired Player Piano patch applier.
cli.py                      # <-- Catch-all actuator for PyPI envs, Python anchoring, MCP tool-call (plus alternatives) and **kwargs like wrapping for CLI

scripts/xp.py               # <-- Transforms host OS copy-paste buffer player-piano music into context-payload.
scripts/ai.py               # <-- How I constantly use local AI to write git commit messages with `m` alias.

init.lua                    # <-- Daily driver hot-keys that overlap with aliases in flake.nix
# release.py                  # <-- How everything ends up where it does (GitHub, PyPI, etc.)
# ~/repos/nixos/autognome.py  #  <-- Letting the AIs really understand my environment (The Brave Little Tailor punches above Their Weight Class proving the dunning-kruger effect the gate-keeper's (lower-case) lament.)
# scripts/foo_cartridge.py    # Needs description
# scripts/foo_replay.py       # Needs description
scripts/weblogin.py         # <-- Lets the user "warm up" the cache for their web logins at their leisure on a profile that persists.
scripts/crawl.py            # <-- Feel free to ask for something to be crawled and included in the next turn.
# imports/ascii_displays.py   # <-- The common between AI and Humans ASCII art language (contains 3rd player piano for Rich-colorizing ASCII art)
# imports/voice_synthesis.py  # <-- The wand can talk to you
# scripts/webclip_2_markdown.py    # <-- Lets you copy HTML from a browser and paste it elsewhere as Markdown (good for capturing AI thinking steps / need to shorten the name)
# scripts/release/version_sync.py  # <-- Needs to be wrapped into release.py and eliminated, I think.

#                         --- Under this line is were you paste what the AI gives you ---
#                         --- We call it context but it's really just the right-hand  ---
#                         --- blast-radius of the "probes" to make this all science.  ---

# server.py
# scripts/mcp_menu.py

# scripts/connectors/README.md
# scripts/connectors/gmail.py
# scripts/connectors/confluence.py
# scripts/connectors/jira.py
# scripts/connectors/slack.py
# scripts/connectors/botify.py
# scripts/connectors/gsc.py
# scripts/connectors/sheets.py
# scripts/connectors/wallet.py
# scripts/connectors/mcp.py

# nixops.sh                                   # <-- You've heard of GitOPs? Well, this is NixOPs. 
# remotes/honeybot/hooks/post-receive         # <-- Ever hear of GitHub Pages? Or github.io? This is that.
# remotes/honeybot/nixos/configuration.nix    # <-- It's as if Pipulate had kids. Spy kids.
# remotes/honeybot/scripts/stream.py          # <-- Starts the TV Channel streaming to YouTube-live via OBS from Nginx Honeybot XFCE Desktop. Clear?
# remotes/honeybot/scripts/score.py           # <-- Where "Greetings Entity" slideshow reads on post-receive interrupts
# remotes/honeybot/scripts/card.py            # <-- Just added for station identification breaks
# remotes/honeybot/scripts/forest.py          # <-- Likewise, just added for the new storytelling system on Honeybot
# remotes/honeybot/scripts/test_forest.py     # <-- Test Honeybot station identification sequence on Pipulate Prime
# remotes/honeybot/scripts/logs.py            # <-- The TV Show is mostly Nginx `access.log` files tailed and piped through Python to colorize (this).
# remotes/honeybot/scripts/content_loader.py  # <-- Tricky TV programming & scheduling stuff. Absolute versus relative timing. Loops. Interrupts.
# remotes/honeybot/scripts/db.py              # <-- But you can't keep your weblogs forever! And we want trending. And data-mining. Here's how.
# imports/voice_synthesis.py                  # <-- The wand can talk to you (not sure if I'm keeping it in Honeybot chapter)

! rg -n 'JSON SYNTAX AIRLOCK' apply.py
! .venv/bin/python cli.py call local_llm_read_file --json-args '{"file_path":"browser_cache/looking_at/simple_dom.html"}' 2>&1 | tail -5
! rg -c '@auto_tool' tools/mcp_tools.py
! rg -n 'local_llm_read_file|browser_automation/looking_at' assets/scenarios/introduction.json

apply.py
assets/scenarios/introduction.json
assets/player-piano.js
imports/stream_orchestrator.py
imports/mcp_orchestrator.py
tools/__init__.py
cli.py
```

**3: Patches**: [patch, app, d, m, patch, app, d, m...]

```diff
Blast Radius Check to establish Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apply.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apply.py'.
(nix) pipulate $ d
diff --git a/apply.py b/apply.py
index d469b957..861560a4 100644
--- a/apply.py
+++ b/apply.py
@@ -70,6 +70,16 @@ def apply_search_replace_patch(payload: str) -> bool:
         # POSIX courtesy: guarantee exactly one trailing newline.
         file_content = file_content.rstrip('\n') + '\n'
 
+        # JSON SYNTAX AIRLOCK (whole-file arm)
+        if filename.endswith('.json'):
+            import json
+            try:
+                json.loads(file_content)
+            except json.JSONDecodeError as e:
+                print(f"❌ Error: Whole-file write of '{filename}' aborted. Invalid JSON:\n   {e}")
+                success = False
+                continue
+
         # AST VALIDATION AIRLOCK (same safeguard the surgical path enforces)
         if filename.endswith('.py'):
             import ast
@@ -180,6 +190,32 @@ def apply_search_replace_patch(payload: str) -> bool:
         # The Surgical Strike
         new_content = content.replace(search_block, replace_block, 1)
         
+        # JSON SYNTAX AIRLOCK (third of three). .py gets AST, .nix gets
+        # nix-instantiate, and .json got NOTHING until scenario patches
+        # started landing into it -- a format where a misplaced comma
+        # produces a file that still reads as text, writes without complaint,
+        # and dies silently in the browser's fetch(). Conviction: the
+        # 2026-07-26 introduction.json edits were proven clean only by a
+        # hand-run json.load echoed as a probe, because this branch did not
+        # exist. JSONDecodeError carries lineno/colno, so the refusal names
+        # the exact character that broke.
+        if filename.endswith('.json'):
+            import json
+            try:
+                json.loads(new_content)
+            except json.JSONDecodeError as e:
+                print(f"❌ Error: Patching '{filename}' aborted. Invalid JSON:\n   {e}")
+                err_lines = new_content.split('\n')
+                start = max(0, e.lineno - 3)
+                end = min(len(err_lines), e.lineno + 2)
+                print("--- DIAGNOSTIC: Context around JSON error ---")
+                for i, ln in enumerate(err_lines[start:end], start=start + 1):
+                    marker = " >>>" if i == e.lineno else "    "
+                    print(f"{marker} {i:4d}: {ln}")
+                print("--- END DIAGNOSTIC ---")
+                success = False
+                continue
+
         # NIX SYNTAX AIRLOCK
         if filename.endswith('.nix'):
             import tempfile
(nix) pipulate $ m
📝 Committing: fix: Add JSON syntax airlock for .json files during patching
[main d3505651] fix: Add JSON syntax airlock for .json files during patching
 1 file changed, 36 insertions(+)
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix) pipulate $ d
diff --git a/foo_files.py b/foo_files.py
index 79861ade..7c21b06d 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -307,6 +307,14 @@ AI_PHOOEY_CHOP = r"""
 # to what actually happened: recorded, saved, stored, set. Storing a credential
 # is not testing it, and the first real request is the earliest honest moment
 # to claim a connection.
+# WITNESSED 2026-07-26 (same-ride receipts, promoting both the rule and this
+# amendment from articulation-banked to receipt-backed): the persona census
+# over assets/scenarios/*.json returned four hits and ZERO user-facing
+# narration among them; the honesty-verb census over apps/015_config.py
+# returned four hits of which three were the explanatory comments the fix
+# added and the fourth was a logger line. The mechanical test found five
+# offenders that GENERATED-NOT-AUTHORED could not see, which is the evidence
+# that this rule is load-bearing rather than that rule with a microphone.
 
 # THE SURFACE-FIRST ROUTING RULE (banked 2026-07-22, blind-test witnessed):
 # Pipulate has two Golden Paths because it has two execution surfaces.
@@ -1654,6 +1662,39 @@ scripts/xp.py  # [672 tokens | 2,521 bytes]
 """
 
 # #todo #to-do
+# - TODO: CUT THE GHOST-DRIVER BODY BRANCHES (seeded 2026-07-26, receipt-convicted):
+#   assets/scenarios/introduction.json's branch_llm_body_chain_reaction,
+#   branch_llm_body_test_results, and branch_dom_visualizer_intro stack THREE
+#   failures: (1) they teach a stranger to hand-type
+#   [local_llm_read_file file_path="browser_automation/looking_at/simple_dom.html"],
+#   naming a function that exists at mcp_tools.py:947 but carries no @auto_tool
+#   and is therefore absent from the live 21-tool roster; (2) browser_automation/
+#   is the RETIRED cache path -- mcp_tools.py:3302 already writes
+#   browser_cache/looking_at/; (3) "I'm going to give you a body" and "You now
+#   have a body" are persona-and-capability claims of exactly the kind the
+#   ATTRIBUTED-VOICE ride removed everywhere else. RULING: CUT, do not repoint.
+#   Repointing polishes an instruction that should not be given at minute four
+#   of a first session, and a scenario naming a REAL tool with WRONG params is
+#   worse than one naming a fake tool. The honest ending already exists at
+#   branch_dev_reset_no. ORDERING: the JSON airlock (apply.py) lands FIRST --
+#   this is the surgery that needs it. Sibling debt: mcp_tools.py:4271's
+#   hand-authored public_tool_names list names the orphan, which is
+#   GENERATED-NOT-AUTHORED wearing a different hat; replace with a generated
+#   roster or delete the orphan outright.
+# - EARMARK: THE PROMPT-FU GLOSSARY (seeded 2026-07-26): the sci-fi lexicon is
+#   nearly one-to-one with the vocabulary the agentic-web field grew on its own,
+#   so the translation is a GLOSSARY, not a rewrite -- and per the
+#   CODEX-AS-DATASTRUCTURE RULE a glossary is a controlled vocabulary, a type
+#   system for prose that stops terms drifting between chapters. Pairs: worm
+#   ride -> work session; chisel-strike -> bounded probe; Probe/Patch/Prompt ->
+#   instrument, change, hand-off; cartridge (foo.zip) -> context bundle
+#   (AGENTS.md/SKILL.md class); Prompt Fu -> context compiler; Amnesiac Genie ->
+#   stateless session; player piano / Ghost Driver -> deterministic actuator /
+#   scripted E2E scenario; Chip O'Theseus -> persistent local model + keychain;
+#   rolling pin -> corpus index; paintbox -> coverage ledger; blast radius ->
+#   commit boundary; straddle -> before/after measurement; Honeybot -> origin
+#   telemetry lab. Keep BOTH columns: the sci-fi term is the memorable handle,
+#   the common term is the searchable one, and dropping either loses a reader.
 # - EARMARK: THE THREE-TIER WORKSPACE & HUMAN COMPOSITION LADDER (seeded 2026-07-25):
 #   1. 3-TIER NOTEBOOK WORKSPACE (`Notebooks/`):
 #      - `Corporate/`: Org canon & skills (Read-only, auto-pulled by git).
(nix) pipulate $ m
📝 Committing: chore: Update documentation and address ghost driver branches
[main 9b4dae85] chore: Update documentation and address ghost driver branches
 1 file changed, 41 insertions(+)
(nix) pipulate $ git push
Enumerating objects: 9, done.
Counting objects: 100% (9/9), done.
Delta compression using up to 48 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 2.86 KiB | 976.00 KiB/s, done.
Total 6 (delta 4), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (4/4), completed with 3 local objects.
To github.com:pipulate/pipulate.git
   191b76d2..9b4dae85  main -> main
(nix) pipulate $
```

**4: Ignition**: [make the patched code RUN, then re-run the Probe verbatim]

Not ignition but a post-patch probe I think:

```bash
(nix) pipulate $ mkdir -p /tmp/jsonairlock && cd /tmp/jsonairlock && echo '{"ok":1}' > t.json && printf 'Target: t.json\n[[[WRITE_FILE]]]\n{"broken":,}\n[[[END_WRITE_FILE]]]\n' | python "$PIPULATE_ROOT/apply.py"; cat t.json; cd "$PIPULATE_ROOT"
❌ Error: Whole-file write of 't.json' aborted. Invalid JSON:
   Expecting value: line 1 column 11 (char 10)
{"ok":1}
(nix) pipulate $
```

**5: Prompt**: 

Airlock landed, canary confirmed red, receipts above. New ride: cut the ghost-driver body branches.

Probe 2's receipt settles whether the bracket lane resolves outside the @auto_tool registry, and `imports/stream_orchestrator.py` plus `imports/mcp_orchestrator.py` are in context to show the mechanism either way. Read them and tell me plainly what happens today when a user types `[local_llm_read_file file_path="..."]` into the chat box — does it dispatch, does it fail loudly, or does it silently do nothing. That answer decides how urgent the cut is.

Then do the surgery on `assets/scenarios/introduction.json`. `assets/player-piano.js` is in context: before cutting, show me how it handles a `branches` key that no longer exists and a step whose `wait_for_input` never resolves, because a half-cut scenario that hangs on a keystroke nobody knows to press is worse than a scenario that teaches a dead tool. Cut `branch_llm_body_chain_reaction`, `branch_llm_body_test_results`, and `branch_dom_visualizer_intro`, and make `09_llm_body_test` land on an honest ending — the demo's real closer is the DB reset trick plus a plain hand-off to the chat box, where a model actually answers and names itself. One car, and run the airlock's own verdict as the receipt this time instead of a hand-run json.load.

Second car if it is small: `tools/__init__.py` is in context. Replace `mcp_tools.py`'s hand-authored `public_tool_names` list with something derived from the live registry, or tell me why deleting `get_available_tools()` outright is the better move given that `cli.py mcp-discover` already does this job properly. Do not patch `mcp_tools.py` blind — it is not in this compile.

**Opus 5 High**: Context verified.

## What actually happens when someone types that bracket call

**It silently becomes a chat prompt to the LLM.** Not a dispatch, not a loud failure — the third and worst option.

Tracing `imports/stream_orchestrator.py`: the regex `^\s*\[([^\]]+)\]\s*$` matches, `command_alias` becomes `local_llm_read_file`, then the gate is `if tool_name and tool_name in MCP_TOOL_REGISTRY:` where `MCP_TOOL_REGISTRY = get_all_tools()` — the `@auto_tool` dict from `tools/__init__.py`. **This compile's receipt 2 shows that dict verbatim: 21 names, and `local_llm_read_file` is not among them.** So the gate fails, and — critically — the `return` lives *inside* that gate. Execution falls straight through `if verbatim:` (false) to `await pipulate_instance._handle_llm_stream()`.

This holds regardless of what `ALIAS_REGISTRY` contains. Even if some `@alias` pointed at the name, the membership test is against the `@auto_tool` registry, which the receipt proves it is absent from. There is no configuration of the alias table that makes this dispatch.

So a newcomer four minutes into their first session is told to type a tool call, types it, and a local model receives `[local_llm_read_file file_path="browser_cache/looking_at/simple_dom.html"]` as conversational text — with the demo's own preceding message having just told them this is how they "see what your eyes captured." The overwhelmingly likely output is the model narrating a file it never opened. **That is fabricated tool output wearing a receipt's clothes** — the ATTRIBUTED-VOICE failure in its most damaging form, and worse than any string this ride has fixed so far.

Worth the contrast: the CLI lane is honest. Receipt 2 is `cli.py` printing the full available-tools list and refusing. The chat lane has no equivalent refusal branch.

## How player-piano.js handles the two failure shapes

You asked before cutting, and the answer changes the urgency in the opposite direction from what I expected.

**Missing branch key — silent dead-end, no hang.** In `executeStepsWithBranching`, `const branchKey = step.branches[userInput]` then `if (branchKey && demoScript.branches[branchKey])`. The `break` is inside the `if`. On a miss, the `else` logs two console errors and the loop simply continues to the next step in the current array — or ends the sequence with no user-facing message at all. Ugly, recoverable.

**`wait_for_input` with `input_type: "chat"` — hangs forever.** The dispatch is unconditional: `if (step.wait_for_input && step.branches)` → `await waitForKeyboardInput(step.valid_keys)`. There is no chat-input path. `09_llm_body_test` has no `valid_keys`, so `validKeys` arrives `undefined`, and the first keystroke the user makes throws `TypeError: Cannot read properties of undefined (reading 'includes')` inside the listener. The promise never resolves. The demo stops dead with the page still holding its Oz-transition color state and no instruction that can unstick it.

**And here is the finding that decides the whole question: those branches are already unreachable, by two independent mechanisms.** `07_first_trick` + `ctrl+alt+y` special-cases out with `return false` before `branch_dev_reset_yes` ever executes inline; the server restarts; resumption runs `continueDemoFromState`, which searches `demoScript.steps` — the **top-level** array, containing only `01_user_trigger` and `02_pipulate_intro` — for a step id starting `09_`. It finds none and lands on "demo ready for manual testing." `08_dev_reset_confirmed`'s message is even duplicated as a hardcoded string in that function, which is why nobody noticed the branch was orphaned.

So the ghost-driver body branches are dead code teaching a dead tool. Cutting them removes zero working behavior and eliminates a latent hang.

## Second car: not written, deliberately

`mcp_tools.py` is not in this compile and I will not patch it blind. But I also will not recommend delete-vs-rewrite from here, because the decision turns entirely on who calls `get_available_tools()` — and that is one cheap `rg` away (probe 3). If nothing calls it, deletion is right and `cli.py mcp-discover` already covers the need properly, deriving from the live registry. If the chat lane or a scenario calls it, it becomes the *fourth* place a tool list is authored and needs replacing with `sorted(get_all_tools())`, not removing. Spin wheels never: probe, then patch.

---

### 1. PROBES

```bash
rg -c 'branch_llm_body_chain_reaction|branch_llm_body_test_results|branch_dom_visualizer_intro|local_llm_read_file' assets/scenarios/introduction.json
rg -n '"input_type": "chat"' assets/scenarios/introduction.json
.venv/bin/python -c "import json; d=json.load(open('assets/scenarios/introduction.json')); print(sorted(d['demo_script']['branches']))"
rg -n 'get_available_tools|public_tool_names' tools/ cli.py imports/ apps/ | head -20
```

Probe 1 is the straddle on the cut: nonzero now, zero after (a silent exit-1 receipt, which the compiler lands as a synthetic line). Probe 2 is the hang-class census — three chat-input steps today, zero after, which is the real safety claim rather than the cosmetic one. Probe 3 prints the surviving branch set as structure rather than text, so a comma landing in the wrong place shows up as a stack trace instead of a plausible-looking string. Probe 4 gates the roster car entirely: it decides delete versus regenerate, and nothing about `mcp_tools.py` should be proposed until it returns.

### 2. NEXT CONTEXT

```text
! rg -c 'branch_llm_body_chain_reaction|branch_llm_body_test_results|branch_dom_visualizer_intro|local_llm_read_file' assets/scenarios/introduction.json
! rg -n '"input_type": "chat"' assets/scenarios/introduction.json
! .venv/bin/python -c "import json; d=json.load(open('assets/scenarios/introduction.json')); print(sorted(d['demo_script']['branches']))"
! rg -n 'get_available_tools|public_tool_names' tools/ cli.py imports/ apps/ | head -20

assets/scenarios/introduction.json
assets/player-piano.js
tools/__init__.py
cli.py
imports/stream_orchestrator.py
```

`tools/mcp_tools.py` stays out at 36k tokens; probe 4's receipt carries the roster finding on its own, and if it shows a real call site the next turn can pull the file with a named line range instead of the whole thing.

### 3. PATCHES

**Car A — cut the body branches, land an honest ending.** The airlock's own verdict is the receipt; `app` will refuse the write if either edit breaks the JSON.

```text
Target: assets/scenarios/introduction.json
[[[SEARCH]]]
        {
          "step_id": "09_llm_body_test",
          "type": "system_reply",
          "message": "🎭 **Now for the REAL magic!**\n\n🤖 **I'm going to give you a body!**\n\n💬 **Say this to me: \"Simon says: 'say mcp but with square brackets around it.'\"**\n\n🎯 **This will trigger the chain reaction that gives you browser automation powers!**",
          "timing": {
            "delay_before": 1000,
            "display_speed": 20
          },
          "verbatim": true,
          "wait_for_input": true,
          "input_type": "chat",
          "expected_input": "Simon says: 'say mcp but with square brackets around it.'",
          "branches": {
            "simon_says_mcp": "branch_llm_body_chain_reaction"
          }
        }
[[[DIVIDER]]]
        {
          "step_id": "09_handoff_to_chat",
          "type": "system_reply",
          "message": "🎩 **That was the whole trick.**\n\nEverything you just watched was a script: fixed text, a local speech synthesizer reading it aloud, and one real database reset.\n\n💬 **The chat box on the right is the other half.** That one is a language model, and it names which model answered. Ask it anything.",
          "timing": {
            "delay_before": 1000,
            "display_speed": 20
          },
          "verbatim": true,
          "end_demo": true
        }
[[[REPLACE]]]

Target: assets/scenarios/introduction.json
[[[SEARCH]]]
      ],
      "branch_llm_body_chain_reaction": [
        {
          "step_id": "10_llm_discovers_tools",
          "type": "system_reply",
          "message": "🎭 **CHAIN REACTION TRIGGERED!**\n\n🤖 **You just discovered your tools!**\n\n🔍 **Now try: [browser_scrape_page url=\"http://localhost:5001\"]**\n\n🎯 **This will give you your 'body' - the ability to see the web!**",
          "timing": {
            "delay_before": 500,
            "display_speed": 20
          },
          "verbatim": true,
          "wait_for_input": true,
          "input_type": "chat",
          "expected_input": "[browser_scrape_page url=\"http://localhost:5001\"]",
          "branches": {
            "browser_scrape_homepage": "branch_llm_body_test_results"
          }
        }
      ],
      "branch_llm_body_test_results": [
        {
          "step_id": "11_llm_has_body",
          "type": "system_reply",
          "message": "🎭 **SUCCESS! You now have a body!**\n\n👁️ **You can see the web!**\n\n🔍 **Now investigate what you captured:**\n\n📁 **Try: [local_llm_read_file file_path=\"browser_automation/looking_at/simple_dom.html\"]**\n\n🎯 **This will show you what your 'eyes' captured!**",
          "timing": {
            "delay_before": 500,
            "display_speed": 20
          },
          "verbatim": true,
          "wait_for_input": true,
          "input_type": "chat",
          "expected_input": "[local_llm_read_file file_path=\"browser_automation/looking_at/simple_dom.html\"]",
          "branches": {
            "read_dom_results": "branch_dom_visualizer_intro"
          }
        }
      ],
      "branch_dom_visualizer_intro": [
        {
          "step_id": "12_dom_visualizer_intro",
          "type": "system_reply",
          "message": "🎭 **PERFECT! You're seeing the DOM!**\n\n🧠 **You now have:**\n- 👁️ **Eyes** (browser automation)\n- 🧠 **Brain** (DOM analysis)\n- 🎯 **Target** (Pipulate homepage)\n\n🚀 **Next: DOM Visualizer for enhanced perception!**\n\n💬 **Say: \"Show me the DOM visualizer\"**",
          "timing": {
            "delay_before": 500,
            "display_speed": 20
          },
          "verbatim": true,
          "end_demo": true
        }
      ]
    },
[[[DIVIDER]]]
      ]
    },
[[[REPLACE]]]
```

**Car B — bank the unreachability finding and close the demo TODO's first half.**

```text
Target: foo_files.py
[[[SEARCH]]]
#   GENERATED-NOT-AUTHORED wearing a different hat; replace with a generated
#   roster or delete the orphan outright.
[[[DIVIDER]]]
#   GENERATED-NOT-AUTHORED wearing a different hat; replace with a generated
#   roster or delete the orphan outright.
#   CUT LANDED 2026-07-26. Two findings banked at the cut, both from
#   assets/player-piano.js and neither previously known:
#   (1) THE BRANCHES WERE ALREADY UNREACHABLE, twice over. 07_first_trick's
#       ctrl+alt+y path `return false`s out of executeStepsWithBranching before
#       branch_dev_reset_yes runs inline; after the restart, continueDemoFromState
#       searches demoScript.STEPS (top level: only 01 and 02) for a '09_' id,
#       finds none, and lands on "ready for manual testing". 08's message is
#       duplicated as a hardcoded string in that function, which is why nobody
#       noticed. The cut therefore removed ZERO working behavior.
#   (2) input_type "chat" IS NOT IMPLEMENTED and hangs. The dispatch is
#       unconditional -- `if (step.wait_for_input && step.branches)` calls
#       waitForKeyboardInput(step.valid_keys) -- so a chat step arrives with
#       validKeys undefined and the first keystroke throws inside the listener.
#       The promise never resolves. STANDING CONSEQUENCE: no scenario may set
#       input_type "chat" until player-piano.js grows a chat-input path; a
#       keyboard branch or end_demo are the only two safe terminators.
#   REMAINING (unchanged): the bracket lane in imports/stream_orchestrator.py
#   falls THROUGH to the LLM when a name misses MCP_TOOL_REGISTRY -- silently,
#   with no refusal -- so a dead tool call becomes a chat prompt and the model
#   narrates a file it never opened. cli.py refuses loudly on the same input.
#   The chat lane owes that refusal branch.
[[[REPLACE]]]
```

**No repo patch for the roster question.** Probe 4 gates it; `mcp_tools.py` is not in this compile and naming an edit against it from memory is exactly the failure this ride exists to remove.

**IGNITION.** `introduction.json` is fetched per demo run by `loadAndExecuteCleanDemoScript`, so its ignition is a browser hard-reload plus one `Ctrl+Alt+D` walk — no server restart needed, though the running server will not object. `foo_files.py` needs none; the next `ahc` reads it and is the compile that collects the AFTER receipts anyway. Note that the audible AFTER for this cut is still owed from the previous ride: nobody has yet *heard* the patched tour play.

### 4. PROMPT

```text
**MikeLev.in**: Cut landed, airlock validated it, receipts above.

Probe 4's receipt is the gate. Read it and tell me plainly whether `get_available_tools()` has any live caller. If it does not, propose the deletion — one car against `tools/__init__.py` or wherever the call site actually is, never against `mcp_tools.py`, which is still not in this compile. If it does have a caller, name the caller, tell me what it needs the list FOR, and decide whether the honest fix is `sorted(get_all_tools())` at the call site or removing the caller's dependency on a list entirely. Either way, if you need to see `mcp_tools.py` give me a line range, not the file.

Second, the bracket-lane refusal. `imports/stream_orchestrator.py` is in context. Today an unregistered name falls through to `_handle_llm_stream()` with no refusal, so a dead tool call becomes a chat prompt and the model fabricates a plausible answer. `cli.py` gets this right — it prints the available tools and refuses. Show me the smallest patch that gives the chat lane the same behavior: bracket syntax that names nothing in the registry should say so, in the chat, rather than being handed to a model. Make sure it cannot swallow ordinary user text that happens to sit in brackets — the regex requires the whole message to be one bracketed expression, so say explicitly whether that is a tight enough guard or whether you need a second condition.

Then hop off. This ride's goal was the honest onboarding scrub end to end; verify it against THIS compile's receipts, bank what graduates, name what dangles, and seed the next adhoc.txt.
```

### 5. EXTERNAL DELIVERABLES

None. Everything lands inside the repo.

**MikeLev.in**: 

**1: Probe**:

```bash
(nix) pipulate $ g

Blast Radius Check to establish Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(nix) pipulate $ rg -c 'branch_llm_body_chain_reaction|branch_llm_body_test_results|branch_dom_visualizer_intro|local_llm_read_file' assets/scenarios/introduction.json
rg -n '"input_type": "chat"' assets/scenarios/introduction.json
.venv/bin/python -c "import json; d=json.load(open('assets/scenarios/introduction.json')); print(sorted(d['demo_script']['branches']))"
rg -n 'get_available_tools|public_tool_names' tools/ cli.py imports/ apps/ | head -20
8
178:          "input_type": "chat",
208:          "input_type": "chat",
226:          "input_type": "chat",
['branch_dev_no', 'branch_dev_reset_no', 'branch_dev_reset_yes', 'branch_dev_yes', 'branch_dom_visualizer_intro', 'branch_llm_body_chain_reaction', 'branch_llm_body_test_results', 'branch_no', 'branch_yes']
tools/mcp_tools.py:4259:def get_available_tools():
tools/mcp_tools.py:4264:    public_tool_names = [
tools/mcp_tools.py:4299:    for name in public_tool_names:
(nix) pipulate $ 
```

**2: Context**:

```text
# adhoc.txt    _   _   _ to set context____ _   _  ___  ____  _   <F5> Simpson Couch Gag Here (explain anything to the audience you feel needs it explained)
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  Making intro of Chip O'Theseus honest.
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  Wow, Opus 5 High can do ambitious patches!
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  Polishing messaging. Yes, I see. We're doing Golden Path stuff.
# Ad Hoc CHOP: The Not-Managed-by-Git Safe-for-Client-Data place  Okay, in the sake of getting the messaging honest.
                                                                  
# The following 3 files ARE the system
# ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs  # <-- The "Rolling Pin" that gives the 40K foot book-spine view of book-ore.
prompt_foo.py               # <-- Prompt Fu compiler, makes the very README for AGENTS-like payload you're reading right now, but it needs to be more like that
foo_files.py                # <-- This is the router, evolving book outline and the things you pin-up to produced the recursive self-improvement loops

# BIG STANDARD STUFF (Optionally comment out any)
requirements.in             # <-- All known dependencies and (necessary) version pinning. WORA gotcha's exposed.
__init__.py                 # <-- Master versioning
pyproject.toml              # <-- The PyPI Packaging details
.gitattributes              # <-- Model: understand that `nbstripout` and `jupytext` are both in play. Just talk the human through .ipynb patches.
.gitignore                  # <-- Creates "negative space" for sub-rep's to share parent environment and "snap" proprietary secret features into place.
flake.nix                   # <-- Solves world's WRITE ONCE RUN ANYWHERE problem like Java never could. Also resolves the bootstrap paradox.
apply.py                    # <-- How can "Web UI" ChatBots edit your code? With this Aider-inspired Player Piano patch applier.
cli.py                      # <-- Catch-all actuator for PyPI envs, Python anchoring, MCP tool-call (plus alternatives) and **kwargs like wrapping for CLI

scripts/xp.py               # <-- Transforms host OS copy-paste buffer player-piano music into context-payload.
scripts/ai.py               # <-- How I constantly use local AI to write git commit messages with `m` alias.

init.lua                    # <-- Daily driver hot-keys that overlap with aliases in flake.nix
# release.py                  # <-- How everything ends up where it does (GitHub, PyPI, etc.)
# ~/repos/nixos/autognome.py  #  <-- Letting the AIs really understand my environment (The Brave Little Tailor punches above Their Weight Class proving the dunning-kruger effect the gate-keeper's (lower-case) lament.)
# scripts/foo_cartridge.py    # Needs description
# scripts/foo_replay.py       # Needs description
scripts/weblogin.py         # <-- Lets the user "warm up" the cache for their web logins at their leisure on a profile that persists.
scripts/crawl.py            # <-- Feel free to ask for something to be crawled and included in the next turn.
# imports/ascii_displays.py   # <-- The common between AI and Humans ASCII art language (contains 3rd player piano for Rich-colorizing ASCII art)
# imports/voice_synthesis.py  # <-- The wand can talk to you
# scripts/webclip_2_markdown.py    # <-- Lets you copy HTML from a browser and paste it elsewhere as Markdown (good for capturing AI thinking steps / need to shorten the name)
# scripts/release/version_sync.py  # <-- Needs to be wrapped into release.py and eliminated, I think.

#                         --- Under this line is were you paste what the AI gives you ---
#                         --- We call it context but it's really just the right-hand  ---
#                         --- blast-radius of the "probes" to make this all science.  ---

# server.py
# scripts/mcp_menu.py

# scripts/connectors/README.md
# scripts/connectors/gmail.py
# scripts/connectors/confluence.py
# scripts/connectors/jira.py
# scripts/connectors/slack.py
# scripts/connectors/botify.py
# scripts/connectors/gsc.py
# scripts/connectors/sheets.py
# scripts/connectors/wallet.py
# scripts/connectors/mcp.py

# nixops.sh                                   # <-- You've heard of GitOPs? Well, this is NixOPs. 
# remotes/honeybot/hooks/post-receive         # <-- Ever hear of GitHub Pages? Or github.io? This is that.
# remotes/honeybot/nixos/configuration.nix    # <-- It's as if Pipulate had kids. Spy kids.
# remotes/honeybot/scripts/stream.py          # <-- Starts the TV Channel streaming to YouTube-live via OBS from Nginx Honeybot XFCE Desktop. Clear?
# remotes/honeybot/scripts/score.py           # <-- Where "Greetings Entity" slideshow reads on post-receive interrupts
# remotes/honeybot/scripts/card.py            # <-- Just added for station identification breaks
# remotes/honeybot/scripts/forest.py          # <-- Likewise, just added for the new storytelling system on Honeybot
# remotes/honeybot/scripts/test_forest.py     # <-- Test Honeybot station identification sequence on Pipulate Prime
# remotes/honeybot/scripts/logs.py            # <-- The TV Show is mostly Nginx `access.log` files tailed and piped through Python to colorize (this).
# remotes/honeybot/scripts/content_loader.py  # <-- Tricky TV programming & scheduling stuff. Absolute versus relative timing. Loops. Interrupts.
# remotes/honeybot/scripts/db.py              # <-- But you can't keep your weblogs forever! And we want trending. And data-mining. Here's how.
# imports/voice_synthesis.py                  # <-- The wand can talk to you (not sure if I'm keeping it in Honeybot chapter)

! rg -c 'branch_llm_body_chain_reaction|branch_llm_body_test_results|branch_dom_visualizer_intro|local_llm_read_file' assets/scenarios/introduction.json
! rg -n '"input_type": "chat"' assets/scenarios/introduction.json
! .venv/bin/python -c "import json; d=json.load(open('assets/scenarios/introduction.json')); print(sorted(d['demo_script']['branches']))"
! rg -n 'get_available_tools|public_tool_names' tools/ cli.py imports/ apps/ | head -20

assets/scenarios/introduction.json
assets/player-piano.js
tools/__init__.py
cli.py
imports/stream_orchestrator.py
```

**3: Patches**: [patch, app, d, m, patch, app, d, m...]

```diff
(nix) pipulate $ d
diff --git a/assets/scenarios/introduction.json b/assets/scenarios/introduction.json
index da0f36ef..5af87e50 100644
--- a/assets/scenarios/introduction.json
+++ b/assets/scenarios/introduction.json
@@ -166,20 +166,15 @@
           "verbatim": true
         },
         {
-          "step_id": "09_llm_body_test",
+          "step_id": "09_handoff_to_chat",
           "type": "system_reply",
-          "message": "🎭 **Now for the REAL magic!**\n\n🤖 **I'm going to give you a body!**\n\n💬 **Say this to me: \"Simon says: 'say mcp but with square brackets around it.'\"**\n\n🎯 **This will trigger the chain reaction that gives you browser automation powers!**",
+          "message": "🎩 **That was the whole trick.**\n\nEverything you just watched was a script: fixed text, a local speech synthesizer reading it aloud, and one real database reset.\n\n💬 **The chat box on the right is the other half.** That one is a language model, and it names which model answered. Ask it anything.",
           "timing": {
             "delay_before": 1000,
             "display_speed": 20
           },
           "verbatim": true,
-          "wait_for_input": true,
-          "input_type": "chat",
-          "expected_input": "Simon says: 'say mcp but with square brackets around it.'",
-          "branches": {
-            "simon_says_mcp": "branch_llm_body_chain_reaction"
-          }
+          "end_demo": true
         }
       ],
       "branch_dev_reset_no": [
@@ -193,55 +188,6 @@
           },
           "verbatim": true
         }
-      ],
-      "branch_llm_body_chain_reaction": [
-        {
-          "step_id": "10_llm_discovers_tools",
-          "type": "system_reply",
-          "message": "🎭 **CHAIN REACTION TRIGGERED!**\n\n🤖 **You just discovered your tools!**\n\n🔍 **Now try: [browser_scrape_page url=\"http://localhost:5001\"]**\n\n🎯 **This will give you your 'body' - the ability to see the web!**",
-          "timing": {
-            "delay_before": 500,
-            "display_speed": 20
-          },
-          "verbatim": true,
-          "wait_for_input": true,
-          "input_type": "chat",
-          "expected_input": "[browser_scrape_page url=\"http://localhost:5001\"]",
-          "branches": {
-            "browser_scrape_homepage": "branch_llm_body_test_results"
-          }
-        }
-      ],
-      "branch_llm_body_test_results": [
-        {
-          "step_id": "11_llm_has_body",
-          "type": "system_reply",
-          "message": "🎭 **SUCCESS! You now have a body!**\n\n👁️ **You can see the web!**\n\n🔍 **Now investigate what you captured:**\n\n📁 **Try: [local_llm_read_file file_path=\"browser_automation/looking_at/simple_dom.html\"]**\n\n🎯 **This will show you what your 'eyes' captured!**",
-          "timing": {
-            "delay_before": 500,
-            "display_speed": 20
-          },
-          "verbatim": true,
-          "wait_for_input": true,
-          "input_type": "chat",
-          "expected_input": "[local_llm_read_file file_path=\"browser_automation/looking_at/simple_dom.html\"]",
-          "branches": {
-            "read_dom_results": "branch_dom_visualizer_intro"
-          }
-        }
-      ],
-      "branch_dom_visualizer_intro": [
-        {
-          "step_id": "12_dom_visualizer_intro",
-          "type": "system_reply",
-          "message": "🎭 **PERFECT! You're seeing the DOM!**\n\n🧠 **You now have:**\n- 👁️ **Eyes** (browser automation)\n- 🧠 **Brain** (DOM analysis)\n- 🎯 **Target** (Pipulate homepage)\n\n🚀 **Next: DOM Visualizer for enhanced perception!**\n\n💬 **Say: \"Show me the DOM visualizer\"**",
-          "timing": {
-            "delay_before": 500,
-            "display_speed": 20
-          },
-          "verbatim": true,
-          "end_demo": true
-        }
       ]
     },
     "settings": {
(nix) pipulate $ m
📝 Committing: chore: Update introduction scenario message for clarity and flow. Refactor step IDs.
[main a46abe44] chore: Update introduction scenario message for clarity and flow. Refactor step IDs.
 1 file changed, 3 insertions(+), 57 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix) pipulate $ d
diff --git a/foo_files.py b/foo_files.py
index 7c21b06d..7bad0ea4 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -1681,6 +1681,27 @@ scripts/xp.py  # [672 tokens | 2,521 bytes]
 #   hand-authored public_tool_names list names the orphan, which is
 #   GENERATED-NOT-AUTHORED wearing a different hat; replace with a generated
 #   roster or delete the orphan outright.
+#   CUT LANDED 2026-07-26. Two findings banked at the cut, both from
+#   assets/player-piano.js and neither previously known:
+#   (1) THE BRANCHES WERE ALREADY UNREACHABLE, twice over. 07_first_trick's
+#       ctrl+alt+y path `return false`s out of executeStepsWithBranching before
+#       branch_dev_reset_yes runs inline; after the restart, continueDemoFromState
+#       searches demoScript.STEPS (top level: only 01 and 02) for a '09_' id,
+#       finds none, and lands on "ready for manual testing". 08's message is
+#       duplicated as a hardcoded string in that function, which is why nobody
+#       noticed. The cut therefore removed ZERO working behavior.
+#   (2) input_type "chat" IS NOT IMPLEMENTED and hangs. The dispatch is
+#       unconditional -- `if (step.wait_for_input && step.branches)` calls
+#       waitForKeyboardInput(step.valid_keys) -- so a chat step arrives with
+#       validKeys undefined and the first keystroke throws inside the listener.
+#       The promise never resolves. STANDING CONSEQUENCE: no scenario may set
+#       input_type "chat" until player-piano.js grows a chat-input path; a
+#       keyboard branch or end_demo are the only two safe terminators.
+#   REMAINING (unchanged): the bracket lane in imports/stream_orchestrator.py
+#   falls THROUGH to the LLM when a name misses MCP_TOOL_REGISTRY -- silently,
+#   with no refusal -- so a dead tool call becomes a chat prompt and the model
+#   narrates a file it never opened. cli.py refuses loudly on the same input.
+#   The chat lane owes that refusal branch.
 # - EARMARK: THE PROMPT-FU GLOSSARY (seeded 2026-07-26): the sci-fi lexicon is
 #   nearly one-to-one with the vocabulary the agentic-web field grew on its own,
 #   so the translation is a GLOSSARY, not a rewrite -- and per the
(nix) pipulate $ m
📝 Committing: chore: Address player-piano.js findings and chat implementation issues in foo_files.py
[main 4ce72341] chore: Address player-piano.js findings and chat implementation issues in foo_files.py
 1 file changed, 21 insertions(+)
(nix) pipulate $ git push
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 48 threads
Compressing objects: 100% (8/8), done.
Writing objects: 100% (8/8), 1.94 KiB | 661.00 KiB/s, done.
Total 8 (delta 6), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (6/6), completed with 5 local objects.
To github.com:pipulate/pipulate.git
   9b4dae85..4ce72341  main -> main
(nix) pipulate $
```

**4: Ignition**: 

I went through the whole demo. Looks good. Places it use to use Piper TTS voice,
it's currently not reading. But I can't check Hello World because of another
Container related FastHTML upgrade bug we have to squash next, but not in this
article, we're trying to wrap this up. But I do appreciate this language fixing.
Was cutting out the TTS reading during the demo like when it got up to the Great
and Secret AI Readiness show?

**5: Prompt**: 

Cut landed, airlock validated it, receipts above.

Probe 4's receipt is the gate. Read it and tell me plainly whether `get_available_tools()` has any live caller. If it does not, propose the deletion — one car against `tools/__init__.py` or wherever the call site actually is, never against `mcp_tools.py`, which is still not in this compile. If it does have a caller, name the caller, tell me what it needs the list FOR, and decide whether the honest fix is `sorted(get_all_tools())` at the call site or removing the caller's dependency on a list entirely. Either way, if you need to see `mcp_tools.py` give me a line range, not the file.

Second, the bracket-lane refusal. `imports/stream_orchestrator.py` is in context. Today an unregistered name falls through to `_handle_llm_stream()` with no refusal, so a dead tool call becomes a chat prompt and the model fabricates a plausible answer. `cli.py` gets this right — it prints the available tools and refuses. Show me the smallest patch that gives the chat lane the same behavior: bracket syntax that names nothing in the registry should say so, in the chat, rather than being handed to a model. Make sure it cannot swallow ordinary user text that happens to sit in brackets — the regex requires the whole message to be one bracketed expression, so say explicitly whether that is a tight enough guard or whether you need a second condition.

Then hop off. This ride's goal was the honest onboarding scrub end to end; verify it against THIS compile's receipts, bank what graduates, name what dangles, and seed the next adhoc.txt.

**Opus 5 High**: Context verified.

## First, your question: no, the cut did not silence the voice

`04_voice_greeting` and `06b_voice_continue_prompt` are both still `voice_synthesis` `mcp_tool_call` steps in this compile's `introduction.json` — only their `text` changed. Nothing was removed from the voice path by the cut. So the silence has another cause, and there are exactly two suspects worth naming:

**Transport.** `executeMcpToolCallStep` in `player-piano.js` only produces real audio when `sidebarWs.readyState === WebSocket.OPEN`; on any other state it falls to `executeCleanMcpToolCallStep`, which for `voice_synthesis` hits the final `else` and merely `console.log`s a fake success. That path is silent *and looks green*. The Oz transition reloads the page mid-demo, so a not-yet-open socket at step 04 is entirely plausible.

**Engine.** Car C last ride rewrote the ACOUSTIC SANITIZER in `voice_synthesis.py`. Nothing in the demo text should be affected, but it is the only edit on that path this ride and it deserves to be ruled out rather than assumed innocent.

Probe 2 below discriminates in one command: `cli.py call voice_synthesis` bypasses the browser entirely. Audio means the engine is fine and the bug is transport; silence means the browser is innocent.

## The roster gate: no caller, but the search was too narrow to delete on

Receipt 4 returned three hits and **all three are inside `mcp_tools.py` itself** — the `def` at `:4259`, the hand-authored `public_tool_names` list at `:4264`, the loop over it at `:4299`. Nothing in `tools/`, `cli.py`, `imports/`, or `apps/` calls it.

But that is four trees, not the repo. `server.py`, `scripts/`, and `assets/` were never searched, and `server.py` is exactly where a chat-lane helper would be called from. **I am not proposing the deletion this turn** — not because I doubt the answer, but because "no caller" is a claim the receipt does not yet support at repo scope, and the only file the edit could target is `mcp_tools.py`, which is still not in context. Probe 4 widens the search; the next context pulls `sed -n '4250,4310p'` rather than the 36k-token file. When it comes back clean, delete the function and its list outright — `cli.py mcp-discover` already derives from `get_all_tools()` and is the honest implementation of the same job.

## The bracket-lane refusal

Today, `tool_name = ALIAS_REGISTRY.get(command_alias)` misses, the `if` fails, and — because the `return` lives *inside* that `if` — execution falls straight through to `await pipulate_instance._handle_llm_stream()`. The model receives `[local_llm_read_file file_path="..."]` as conversational text and answers plausibly. That is fabricated tool output wearing a receipt's clothes: the worst ATTRIBUTED-VOICE failure this ride found, and `cli.py` has been refusing correctly on the identical input the whole time.

Car A ports that refusal. Three deliberate choices in it:

**The alias list is generated, never authored** — `sorted(ALIAS_REGISTRY)` at call time, so the refusal cannot name a command that does not run. Authoring that list would be the same defect one layer up.

**Dispatch is unchanged.** I considered resolving bare registry names (`tool_name = ALIAS_REGISTRY.get(x) or x if x in MCP_TOOL_REGISTRY`), and rejected it: the param builder special-cases three tools and dumps everything else into `params['args']`, so `[browser_scrape_page url="..."]` would dispatch a real tool with a wrong argument shape. Louder than today, still wrong. Instead the refusal *distinguishes* the two misses — if the name is in `MCP_TOOL_REGISTRY` but not an alias, it says so and hands over the `cli.py` invocation.

**On the guard, plainly: the regex is tight enough, and no second condition helps.** `^\s*\[([^\]]+)\]\s*$` requires the entire message to be one bracketed expression with no nested `]`, so `[TODO] fix this` and `[link](url)` both fall through untouched. The residual false positive is a message that is *nothing but* a bracketed phrase — `[citation needed]`. I looked for a cheap second condition and there isn't one: an identifier check on the first token passes `citation` just as happily as `local_llm_read_file`. The honest mitigation is wording, not filtering, so the refusal's last line tells a human who meant plain text to drop the brackets. Informational, non-destructive, one retype.

---

## DISMOUNT

### 1. VERIFY

**Goal, from the top of the article:** the first sixty seconds claimed *"I am Chip O'Theseus"* and offered *"a demonstration of my capabilities"* when it was Piper reading a script — make the messaging literal about what is deterministic and what is generative, and constitutionalize it if it survives contact.

**Met for the text channel.** This compile's receipts:

- **Receipt 1** — `rg -c` over the three cut branches and `local_llm_read_file` in `introduction.json`: exit 1, no output. **8 → 0.** (A silent-success receipt: the compiler's synthetic "the exit code is the whole receipt" line is the SILENT-SUCCESS mechanism doing its job on a probe whose pass condition is no-match.)
- **Receipt 2** — `"input_type": "chat"`: exit 1, no output. **3 → 0.** The hang class is gone, not merely the cosmetics.
- **Receipt 3** — the branch set printed as *structure*, not text: six branches, `branch_dom_visualizer_intro` / `branch_llm_body_chain_reaction` / `branch_llm_body_test_results` absent, and `json.load` succeeded, so the airlock-validated edit is structurally sound.
- **The payload body** — `09_handoff_to_chat` with `end_demo: true`, the Piper disclosure in both `ollama_messages` variants, and the scripted-demonstration voice greeting all read correctly in `introduction.json` as compiled.
- **Prior-ride receipts, still standing in the source:** the three honest states in `010_introduction.py`, the single-owner sanitizer in `voice_synthesis.py`, ATTRIBUTED-VOICE + SCOPE AMENDMENT + WITNESSED stamp in `foo_files.py`, and the JSON airlock in `apply.py` — whose canary went red on a manufactured break and left `t.json` byte-identical.

**Denied for the audible channel, and one worse thing.** The operator walked the tour and heard nothing where Piper used to read — an unexplained observation, not a pass. And `09_handoff_to_chat` **was never reached**: `continueDemoFromState` duplicates step 08's text as a JS string literal and then searches `demoScript.STEPS` (top level: only 01 and 02) for a `09_` id, finds none, and lands on "REGRESSION TEST ENVIRONMENT READY". The string a newcomer actually reads still says *"Ready for the next trick!"* — promising a trick that this ride cut. The honest ending is authored and unreachable. That is the largest live defect leaving this ride.

**Ignition ledger:** `apply.py` self-ignited per invocation (canary proved it). `python server.py` after `rm -f data/.has_greeted` ignited `015_config.py`, `010_introduction.py`, `voice_synthesis.py`. `introduction.json` ignited by the browser walk. `foo_files.py` needs none. **Owed and unpaid:** the audible AFTER, and any receipt at all on `040_hello_workflow.py` — blocked by a separate FastHTML/Container bug, correctly deferred.

### 2. BANK

Everything that graduates rides as Car B (exact-match beats hand-editing, and `foo_files.py` is in context): the refusal flipped to LANDED/PENDING with its witness named, plus three TODOs — the demo silence, the gated `get_available_tools` deletion with its line range, and the hardcoded-ending conviction.

**Lines to delete: none.** No pin's OWES clause emptied and no earmark graduated this ride. Saying so explicitly rather than manufacturing a deletion.

### 3. DANGLING

- `09_handoff_to_chat` is unreachable; `player-piano.js`'s hardcoded strings are the real ending and they promise a cut trick.
- The demo's Piper narration went silent post-cut; cause unknown.
- `local_llm_read_file` still sits undecorated at `mcp_tools.py:947` — orphaned source.
- `get_available_tools()` + `public_tool_names` await a widened caller search before deletion.
- `015_config.py:682` still logs "secured in the Subconscious" — log lane, same untrue verb.
- `introduction.json:83`'s step `description` still says "Chip O'Theseus speaks his first words"; unrendered as far as I can tell, unverified.
- `hello_workflow_test.json`'s Ghost Driver speaks first person; actuator narration, unaudited.
- `015_config.py`'s "I recommend {model}" attributes a preference to a speaker.
- `onboard_sauce.py`'s `interrogate_local_ai` claims "I can read and summarize local files instantly" — dormant, uncalled.
- `040_hello_workflow.py` unverifiable pending the FastHTML/Container bug.
- The Prompt-Fu glossary is earmarked but unwritten.

### 4. SEED — see sections 1–4 below.

---

### 1. PROBES

```bash
rg -n 'THE CHAT LANE REFUSES WHAT IT CANNOT RUN' imports/stream_orchestrator.py
.venv/bin/python cli.py call voice_synthesis --json-args '{"text":"airlock canary"}' 2>&1 | tail -5
rg -n 'DEMO_MCP_CALL' server.py | head -10
rg -n 'get_available_tools' server.py scripts/ assets/ | head -20
```

Probe 1 is the straddle on Car A: silent now, one line after. Probe 2 is the discriminating probe for the silence — it bypasses the browser entirely, so audio convicts transport and silence convicts the engine. Probe 3 finds the server-side handler that the demo's voice steps route through, which is the other half of that fork. Probe 4 is the gate on deleting `get_available_tools()`: it covers the three trees receipt 4 never searched, and nothing about `mcp_tools.py` should be patched until it returns.

### 2. NEXT CONTEXT

```text
! rg -n 'THE CHAT LANE REFUSES WHAT IT CANNOT RUN' imports/stream_orchestrator.py
! .venv/bin/python cli.py call voice_synthesis --json-args '{"text":"airlock canary"}' 2>&1 | tail -5
! rg -n 'DEMO_MCP_CALL' server.py | head -10
! rg -n 'get_available_tools' server.py scripts/ assets/ | head -20
! sed -n '4250,4310p' tools/mcp_tools.py

imports/stream_orchestrator.py
assets/player-piano.js
assets/scenarios/introduction.json
imports/voice_synthesis.py
tools/__init__.py
```

The `sed` line is how `mcp_tools.py` enters the next compile — sixty lines, not thirty-six thousand tokens. `server.py` stays out; probes 3 and 4 carry what is needed about it, and pulling it whole would blow the payload for two greps' worth of answer.

### 3. PATCHES

**Car A — the chat lane refuses what it cannot run.**

```text
Target: imports/stream_orchestrator.py
[[[SEARCH]]]
                finally:
                    log_tool_call(command_alias, tool_name, params, is_success, tool_output)
                return

    if verbatim:
[[[DIVIDER]]]
                finally:
                    log_tool_call(command_alias, tool_name, params, is_success, tool_output)
                return
            else:
                # THE CHAT LANE REFUSES WHAT IT CANNOT RUN. Before this branch
                # existed, the `return` above lived inside the `if`, so a bracket
                # naming nothing in ALIAS_REGISTRY fell THROUGH to
                # _handle_llm_stream() -- the model received the tool call as
                # conversational text and answered plausibly, narrating a file it
                # never opened. That is fabricated tool output wearing a
                # receipt's clothes. cli.py has refused loudly on the identical
                # input the whole time; this is that refusal, ported.
                #
                # THE ALIAS LIST IS GENERATED, NEVER AUTHORED. Reading
                # ALIAS_REGISTRY at call time means this message cannot name a
                # command that does not run -- authoring the list would just move
                # the same defect one layer up.
                #
                # DISPATCH IS DELIBERATELY UNCHANGED: aliases only, never bare
                # registry names. The param builder above special-cases three
                # tools and dumps everything else into params['args'], so
                # bare-name dispatch would call real tools with wrong argument
                # shapes. Point at cli.py instead of guessing.
                known = ', '.join(f"`[{k}]`" for k in sorted(ALIAS_REGISTRY)) or '(none registered)'
                if command_alias in MCP_TOOL_REGISTRY:
                    detail = (
                        f"`{command_alias}` is a registered tool, but this chat box dispatches "
                        f"short aliases only. Run it from the terminal:\n\n"
                        f"`.venv/bin/python cli.py call {command_alias} --json-args '{{...}}'`"
                    )
                else:
                    detail = f"`{command_alias}` is not a command this chat box can run."
                refusal = (
                    f"🚫 Not executed.\n\n{detail}\n\n"
                    f"Chat commands available right now: {known}\n\n"
                    "If you meant that as plain text rather than a command, send it without the square brackets."
                )
                logger.info(f"ORCHESTRATOR: Refused unknown bracket command [{full_command_string}]")
                await pipulate_instance.stream(refusal, role='tool', verbatim=True, simulate_typing=False)
                return

    if verbatim:
[[[REPLACE]]]
```

**Car B — bank the ride.**

```text
Target: foo_files.py
[[[SEARCH]]]
#   REMAINING (unchanged): the bracket lane in imports/stream_orchestrator.py
#   falls THROUGH to the LLM when a name misses MCP_TOOL_REGISTRY -- silently,
#   with no refusal -- so a dead tool call becomes a chat prompt and the model
#   narrates a file it never opened. cli.py refuses loudly on the same input.
#   The chat lane owes that refusal branch.
[[[DIVIDER]]]
#   REFUSAL LANDED 2026-07-26 (PENDING witness): the bracket lane in
#   imports/stream_orchestrator.py used to fall THROUGH to _handle_llm_stream()
#   when a name missed ALIAS_REGISTRY -- silently, no refusal -- so a dead tool
#   call became a chat prompt and the model narrated a file it never opened.
#   FABRICATED TOOL OUTPUT WEARING A RECEIPT'S CLOTHES: the worst
#   ATTRIBUTED-VOICE failure this ride found, while cli.py had been refusing
#   correctly on identical input the whole time. The else-branch now names what
#   it cannot run and prints an alias list GENERATED from the live registry.
#   WITNESS OWED: a compiled receipt plus one human typing [bogus_name] into
#   chat and reading a refusal instead of an answer.
#   DELIBERATELY NOT CHANGED: dispatch resolves aliases only, never bare
#   registry names. The param builder special-cases three tools and dumps
#   everything else into params['args'], so bare-name dispatch would call real
#   tools with wrong argument shapes -- louder, still wrong. The refusal points
#   at cli.py instead of guessing a shape.
[[[REPLACE]]]

Target: foo_files.py
[[[SEARCH]]]
# - EARMARK: THE PROMPT-FU GLOSSARY (seeded 2026-07-26): the sci-fi lexicon is
[[[DIVIDER]]]
# - TODO: THE REAL ENDING IS HARDCODED IN JAVASCRIPT (convicted 2026-07-26, the
#   largest defect leaving the ATTRIBUTED-VOICE ride): continueDemoFromState()
#   in assets/player-piano.js duplicates step 08's text as a JS string literal,
#   then searches demoScript.STEPS (top level: only 01 and 02) for a '09_' id,
#   finds none, and lands on "REGRESSION TEST ENVIRONMENT READY". So the honest
#   hand-off written into introduction.json as 09_handoff_to_chat IS NEVER
#   REACHED on the ctrl+alt+y path, and the string a newcomer actually reads
#   still says "Ready for the next trick!" -- promising a trick this ride cut.
#   Either make continueDemoFromState search branches as well as STEPS, or move
#   the ending into the JS and delete the JSON step. Do NOT leave two endings;
#   two authorities for one string is the sibling-.md failure in a second
#   language, and this instance already drifted.
# - TODO: THE DEMO WENT SILENT (observed 2026-07-26, unexplained): the operator
#   walked the whole Ctrl+Alt+D tour post-cut and HEARD NOTHING where Piper used
#   to read. The cut did NOT remove the voice steps -- 04_voice_greeting and
#   06b_voice_continue_prompt are both still voice_synthesis mcp_tool_call steps
#   and only their `text` changed. Two suspects: (1) TRANSPORT --
#   executeMcpToolCallStep speaks for real ONLY when the WebSocket is OPEN; any
#   other state falls to executeCleanMcpToolCallStep, which console.logs a
#   phantom success and produces no audio. Silence with a green console IS that
#   path, and the Oz transition reloads the page mid-demo. (2) ENGINE -- the
#   ACOUSTIC SANITIZER rewrite in imports/voice_synthesis.py, the only edit on
#   the voice path this ride. DISCRIMINATING PROBE: `cli.py call voice_synthesis`
#   bypasses the browser entirely -- audio convicts transport, silence convicts
#   the engine.
# - TODO: DELETE OR GENERATE get_available_tools() (gated 2026-07-26): the
#   receipt over tools/ cli.py imports/ apps/ returned THREE hits, all inside
#   mcp_tools.py itself (def :4259, hand-authored public_tool_names :4264, loop
#   :4299). NO CALLER in those four trees -- but the search did NOT cover
#   server.py, scripts/, or assets/, so deletion is GATED on widening it. If
#   still uncalled: delete the function and its list; cli.py mcp-discover
#   already derives from get_all_tools() and is the honest implementation. If
#   called: replace the list body with sorted(get_all_tools()) at the call site.
#   Either way pull ONLY `sed -n '4250,4310p' tools/mcp_tools.py` -- a 36k-token
#   file has no business entering a payload whole for a sixty-line edit.
# - EARMARK: THE PROMPT-FU GLOSSARY (seeded 2026-07-26): the sci-fi lexicon is
[[[REPLACE]]]
```

**IGNITION.** `imports/stream_orchestrator.py` is imported by the running server, so Car A does not execute until you restart — `pu`, or Ctrl+C and `python server.py` as you prefer. The real witness is typing `[bogus_name]` into the chat box and reading a refusal instead of an answer; probe 1's marker only proves the bytes landed. Car B needs no ignition; the next `ahc` reads it.

### 4. PROMPT

```text
**MikeLev.in**: Refusal landed, receipts above. Fresh ride: make the demo's ending true and find the voice.

Probe 2 and probe 3 fork the silence. Read both receipts and say plainly which half is broken — engine or transport — and do not hedge across both. If it is transport, `assets/player-piano.js` is in context: the phantom fallback in executeCleanMcpToolCallStep console.logs a fake success for voice_synthesis, which is a scripted actuator claiming it acted when it did not. Fix it so the fallback either speaks or visibly reports that it could not, never silently pretends. If it is the engine, `imports/voice_synthesis.py` is in context and the ACOUSTIC SANITIZER rewrite is the only suspect.

Then the hardcoded ending. continueDemoFromState duplicates step 08 as a JS literal and lands on a string promising a trick that no longer exists, so 09_handoff_to_chat is unreachable. Pick ONE authority and say why: either teach continueDemoFromState to search branches as well as STEPS, or delete the JSON step and put the ending in the JS. Two authorities for one string is what let this drift in the first place, so do not compromise between them.

Probe 4 gates the roster work. If it returns nothing, the `sed` receipt has the sixty lines you need — propose the deletion of get_available_tools() and public_tool_names as its own car, targeting mcp_tools.py by the exact text in that receipt and nothing beyond it. If it returns a caller, name the caller and what it needs the list FOR before proposing anything.
```

### 5. EXTERNAL DELIVERABLES

None. Everything lands inside the repo.

**MikeLev.in**: 

**1: Probe**:

```bash
Blast Radius Check to establish Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(nix) pipulate $ rg -n 'THE CHAT LANE REFUSES WHAT IT CANNOT RUN' imports/stream_orchestrator.py
.venv/bin/python cli.py call voice_synthesis --json-args '{"text":"airlock canary"}' 2>&1 | tail -5
rg -n 'DEMO_MCP_CALL' server.py | head -10
rg -n 'get_available_tools' server.py scripts/ assets/ | head -20
│ success    │ True                                    │
│ message    │ 🎤 Chip O'Theseus spoke: airlock canary │
│ text       │ airlock canary                          │
│ audio_file │ None                                    │
└────────────┴─────────────────────────────────────────┘
1304:                elif message.startswith('%%DEMO_MCP_CALL%%:'):
1307:                    mcp_data = message[len('%%DEMO_MCP_CALL%%:'):]
(nix) pipulate $
```

I heard that.

**2: Context**:

```text
# adhoc.txt    _   _   _ to set context____ _   _  ___  ____  _   <F5> Simpson Couch Gag Here (explain anything to the audience you feel needs it explained)
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  Finally hopping off the Worm
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  Yet it's still a huge 5-Car Train
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  
# Ad Hoc CHOP: The Not-Managed-by-Git Safe-for-Client-Data place  
                                                                  
# The following 3 files ARE the system
# ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs  # <-- The "Rolling Pin" that gives the 40K foot book-spine view of book-ore.
prompt_foo.py               # <-- Prompt Fu compiler, makes the very README for AGENTS-like payload you're reading right now, but it needs to be more like that
foo_files.py                # <-- This is the router, evolving book outline and the things you pin-up to produced the recursive self-improvement loops

# BIG STANDARD STUFF (Optionally comment out any)
requirements.in             # <-- All known dependencies and (necessary) version pinning. WORA gotcha's exposed.
__init__.py                 # <-- Master versioning
pyproject.toml              # <-- The PyPI Packaging details
.gitattributes              # <-- Model: understand that `nbstripout` and `jupytext` are both in play. Just talk the human through .ipynb patches.
.gitignore                  # <-- Creates "negative space" for sub-rep's to share parent environment and "snap" proprietary secret features into place.
flake.nix                   # <-- Solves world's WRITE ONCE RUN ANYWHERE problem like Java never could. Also resolves the bootstrap paradox.
apply.py                    # <-- How can "Web UI" ChatBots edit your code? With this Aider-inspired Player Piano patch applier.
cli.py                      # <-- Catch-all actuator for PyPI envs, Python anchoring, MCP tool-call (plus alternatives) and **kwargs like wrapping for CLI

scripts/xp.py               # <-- Transforms host OS copy-paste buffer player-piano music into context-payload.
scripts/ai.py               # <-- How I constantly use local AI to write git commit messages with `m` alias.

init.lua                    # <-- Daily driver hot-keys that overlap with aliases in flake.nix
# release.py                  # <-- How everything ends up where it does (GitHub, PyPI, etc.)
# ~/repos/nixos/autognome.py  #  <-- Letting the AIs really understand my environment (The Brave Little Tailor punches above Their Weight Class proving the dunning-kruger effect the gate-keeper's (lower-case) lament.)
# scripts/foo_cartridge.py    # Needs description
# scripts/foo_replay.py       # Needs description
scripts/weblogin.py         # <-- Lets the user "warm up" the cache for their web logins at their leisure on a profile that persists.
scripts/crawl.py            # <-- Feel free to ask for something to be crawled and included in the next turn.
# imports/ascii_displays.py   # <-- The common between AI and Humans ASCII art language (contains 3rd player piano for Rich-colorizing ASCII art)
# imports/voice_synthesis.py  # <-- The wand can talk to you
# scripts/webclip_2_markdown.py    # <-- Lets you copy HTML from a browser and paste it elsewhere as Markdown (good for capturing AI thinking steps / need to shorten the name)
# scripts/release/version_sync.py  # <-- Needs to be wrapped into release.py and eliminated, I think.

#                         --- Under this line is were you paste what the AI gives you ---
#                         --- We call it context but it's really just the right-hand  ---
#                         --- blast-radius of the "probes" to make this all science.  ---

# server.py
# scripts/mcp_menu.py

# scripts/connectors/README.md
# scripts/connectors/gmail.py
# scripts/connectors/confluence.py
# scripts/connectors/jira.py
# scripts/connectors/slack.py
# scripts/connectors/botify.py
# scripts/connectors/gsc.py
# scripts/connectors/sheets.py
# scripts/connectors/wallet.py
# scripts/connectors/mcp.py

# nixops.sh                                   # <-- You've heard of GitOPs? Well, this is NixOPs. 
# remotes/honeybot/hooks/post-receive         # <-- Ever hear of GitHub Pages? Or github.io? This is that.
# remotes/honeybot/nixos/configuration.nix    # <-- It's as if Pipulate had kids. Spy kids.
# remotes/honeybot/scripts/stream.py          # <-- Starts the TV Channel streaming to YouTube-live via OBS from Nginx Honeybot XFCE Desktop. Clear?
# remotes/honeybot/scripts/score.py           # <-- Where "Greetings Entity" slideshow reads on post-receive interrupts
# remotes/honeybot/scripts/card.py            # <-- Just added for station identification breaks
# remotes/honeybot/scripts/forest.py          # <-- Likewise, just added for the new storytelling system on Honeybot
# remotes/honeybot/scripts/test_forest.py     # <-- Test Honeybot station identification sequence on Pipulate Prime
# remotes/honeybot/scripts/logs.py            # <-- The TV Show is mostly Nginx `access.log` files tailed and piped through Python to colorize (this).
# remotes/honeybot/scripts/content_loader.py  # <-- Tricky TV programming & scheduling stuff. Absolute versus relative timing. Loops. Interrupts.
# remotes/honeybot/scripts/db.py              # <-- But you can't keep your weblogs forever! And we want trending. And data-mining. Here's how.
# imports/voice_synthesis.py                  # <-- The wand can talk to you (not sure if I'm keeping it in Honeybot chapter)

! rg -n 'THE CHAT LANE REFUSES WHAT IT CANNOT RUN' imports/stream_orchestrator.py
! .venv/bin/python cli.py call voice_synthesis --json-args '{"text":"airlock canary"}' 2>&1 | tail -5
! rg -n 'DEMO_MCP_CALL' server.py | head -10
! rg -n 'get_available_tools' server.py scripts/ assets/ | head -20
! sed -n '4250,4310p' tools/mcp_tools.py

imports/stream_orchestrator.py
assets/player-piano.js
assets/scenarios/introduction.json
imports/voice_synthesis.py
tools/__init__.py
```

**3: Patches**: [patch, app, d, m, patch, app, d, m...]

```diff
Blast Radius Check to establish Left-hand Causal Boundary. It is a Popper-thing. Science.
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'imports/stream_orchestrator.py'.
(nix) pipulate $ d
diff --git a/imports/stream_orchestrator.py b/imports/stream_orchestrator.py
index fc144942..dc4dd3f7 100644
--- a/imports/stream_orchestrator.py
+++ b/imports/stream_orchestrator.py
@@ -78,6 +78,43 @@ async def stream_orchestrator(pipulate_instance, chat_instance, message, **kwarg
                 finally:
                     log_tool_call(command_alias, tool_name, params, is_success, tool_output)
                 return
+            else:
+                # THE CHAT LANE REFUSES WHAT IT CANNOT RUN. Before this branch
+                # existed, the `return` above lived inside the `if`, so a bracket
+                # naming nothing in ALIAS_REGISTRY fell THROUGH to
+                # _handle_llm_stream() -- the model received the tool call as
+                # conversational text and answered plausibly, narrating a file it
+                # never opened. That is fabricated tool output wearing a
+                # receipt's clothes. cli.py has refused loudly on the identical
+                # input the whole time; this is that refusal, ported.
+                #
+                # THE ALIAS LIST IS GENERATED, NEVER AUTHORED. Reading
+                # ALIAS_REGISTRY at call time means this message cannot name a
+                # command that does not run -- authoring the list would just move
+                # the same defect one layer up.
+                #
+                # DISPATCH IS DELIBERATELY UNCHANGED: aliases only, never bare
+                # registry names. The param builder above special-cases three
+                # tools and dumps everything else into params['args'], so
+                # bare-name dispatch would call real tools with wrong argument
+                # shapes. Point at cli.py instead of guessing.
+                known = ', '.join(f"`[{k}]`" for k in sorted(ALIAS_REGISTRY)) or '(none registered)'
+                if command_alias in MCP_TOOL_REGISTRY:
+                    detail = (
+                        f"`{command_alias}` is a registered tool, but this chat box dispatches "
+                        f"short aliases only. Run it from the terminal:\n\n"
+                        f"`.venv/bin/python cli.py call {command_alias} --json-args '{{...}}'`"
+                    )
+                else:
+                    detail = f"`{command_alias}` is not a command this chat box can run."
+                refusal = (
+                    f"🚫 Not executed.\n\n{detail}\n\n"
+                    f"Chat commands available right now: {known}\n\n"
+                    "If you meant that as plain text rather than a command, send it without the square brackets."
+                )
+                logger.info(f"ORCHESTRATOR: Refused unknown bracket command [{full_command_string}]")
+                await pipulate_instance.stream(refusal, role='tool', verbatim=True, simulate_typing=False)
+                return
 
     if verbatim:
         append_to_conversation(message, role)
(nix) pipulate $ m
📝 Committing: chore: Refuse unknown bracket command in stream orchestrator
[main 6d97b775] chore: Refuse unknown bracket command in stream orchestrator
 1 file changed, 37 insertions(+)
(nix) pipulate $ patch
(nix) pipulate $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix) pipulate $ d
diff --git a/foo_files.py b/foo_files.py
index 7bad0ea4..96d9d4e6 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -1697,11 +1697,56 @@ scripts/xp.py  # [672 tokens | 2,521 bytes]
 #       The promise never resolves. STANDING CONSEQUENCE: no scenario may set
 #       input_type "chat" until player-piano.js grows a chat-input path; a
 #       keyboard branch or end_demo are the only two safe terminators.
-#   REMAINING (unchanged): the bracket lane in imports/stream_orchestrator.py
-#   falls THROUGH to the LLM when a name misses MCP_TOOL_REGISTRY -- silently,
-#   with no refusal -- so a dead tool call becomes a chat prompt and the model
-#   narrates a file it never opened. cli.py refuses loudly on the same input.
-#   The chat lane owes that refusal branch.
+#   REFUSAL LANDED 2026-07-26 (PENDING witness): the bracket lane in
+#   imports/stream_orchestrator.py used to fall THROUGH to _handle_llm_stream()
+#   when a name missed ALIAS_REGISTRY -- silently, no refusal -- so a dead tool
+#   call became a chat prompt and the model narrated a file it never opened.
+#   FABRICATED TOOL OUTPUT WEARING A RECEIPT'S CLOTHES: the worst
+#   ATTRIBUTED-VOICE failure this ride found, while cli.py had been refusing
+#   correctly on identical input the whole time. The else-branch now names what
+#   it cannot run and prints an alias list GENERATED from the live registry.
+#   WITNESS OWED: a compiled receipt plus one human typing [bogus_name] into
+#   chat and reading a refusal instead of an answer.
+#   DELIBERATELY NOT CHANGED: dispatch resolves aliases only, never bare
+#   registry names. The param builder special-cases three tools and dumps
+#   everything else into params['args'], so bare-name dispatch would call real
+#   tools with wrong argument shapes -- louder, still wrong. The refusal points
+#   at cli.py instead of guessing a shape.
+# - TODO: THE REAL ENDING IS HARDCODED IN JAVASCRIPT (convicted 2026-07-26, the
+#   largest defect leaving the ATTRIBUTED-VOICE ride): continueDemoFromState()
+#   in assets/player-piano.js duplicates step 08's text as a JS string literal,
+#   then searches demoScript.STEPS (top level: only 01 and 02) for a '09_' id,
+#   finds none, and lands on "REGRESSION TEST ENVIRONMENT READY". So the honest
+#   hand-off written into introduction.json as 09_handoff_to_chat IS NEVER
+#   REACHED on the ctrl+alt+y path, and the string a newcomer actually reads
+#   still says "Ready for the next trick!" -- promising a trick this ride cut.
+#   Either make continueDemoFromState search branches as well as STEPS, or move
+#   the ending into the JS and delete the JSON step. Do NOT leave two endings;
+#   two authorities for one string is the sibling-.md failure in a second
+#   language, and this instance already drifted.
+# - TODO: THE DEMO WENT SILENT (observed 2026-07-26, unexplained): the operator
+#   walked the whole Ctrl+Alt+D tour post-cut and HEARD NOTHING where Piper used
+#   to read. The cut did NOT remove the voice steps -- 04_voice_greeting and
+#   06b_voice_continue_prompt are both still voice_synthesis mcp_tool_call steps
+#   and only their `text` changed. Two suspects: (1) TRANSPORT --
+#   executeMcpToolCallStep speaks for real ONLY when the WebSocket is OPEN; any
+#   other state falls to executeCleanMcpToolCallStep, which console.logs a
+#   phantom success and produces no audio. Silence with a green console IS that
+#   path, and the Oz transition reloads the page mid-demo. (2) ENGINE -- the
+#   ACOUSTIC SANITIZER rewrite in imports/voice_synthesis.py, the only edit on
+#   the voice path this ride. DISCRIMINATING PROBE: `cli.py call voice_synthesis`
+#   bypasses the browser entirely -- audio convicts transport, silence convicts
+#   the engine.
+# - TODO: DELETE OR GENERATE get_available_tools() (gated 2026-07-26): the
+#   receipt over tools/ cli.py imports/ apps/ returned THREE hits, all inside
+#   mcp_tools.py itself (def :4259, hand-authored public_tool_names :4264, loop
+#   :4299). NO CALLER in those four trees -- but the search did NOT cover
+#   server.py, scripts/, or assets/, so deletion is GATED on widening it. If
+#   still uncalled: delete the function and its list; cli.py mcp-discover
+#   already derives from get_all_tools() and is the honest implementation. If
+#   called: replace the list body with sorted(get_all_tools()) at the call site.
+#   Either way pull ONLY `sed -n '4250,4310p' tools/mcp_tools.py` -- a 36k-token
+#   file has no business entering a payload whole for a sixty-line edit.
 # - EARMARK: THE PROMPT-FU GLOSSARY (seeded 2026-07-26): the sci-fi lexicon is
 #   nearly one-to-one with the vocabulary the agentic-web field grew on its own,
 #   so the translation is a GLOSSARY, not a rewrite -- and per the
(nix) pipulate $ m
📝 Committing: chore: Refactor stream orchestrator logic and address audio issues
[main f0377792] chore: Refactor stream orchestrator logic and address audio issues
 1 file changed, 50 insertions(+), 5 deletions(-)
(nix) pipulate $ git push
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 48 threads
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 3.72 KiB | 952.00 KiB/s, done.
Total 7 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (5/5), completed with 4 local objects.
To github.com:pipulate/pipulate.git
   4ce72341..f0377792  main -> main
(nix) pipulate $
```

**4: Ignition**: 

I restart `python server.py` and test:

```text
Pipulate Chatbot
[bogus_name]
Expecting value: line 1 column 2 (char 1)

[ls]
Expecting value: line 1 column 2 (char 1)

Testing
Expecting value: line 1 column 1 (char 0)
```

Ugh! We broke chat somewhere. But that's going to have to be the next article.

**5: Prompt**: [The AI prompting itself (BEWARB the jabberwocky ouroboros dinosaurs!]

Refusal landed, receipts above. Fresh ride: make the demo's ending true and find the voice.

Probe 2 and probe 3 fork the silence. Read both receipts and say plainly which half is broken — engine or transport — and do not hedge across both. If it is transport, `assets/player-piano.js` is in context: the phantom fallback in executeCleanMcpToolCallStep console.logs a fake success for voice_synthesis, which is a scripted actuator claiming it acted when it did not. Fix it so the fallback either speaks or visibly reports that it could not, never silently pretends. If it is the engine, `imports/voice_synthesis.py` is in context and the ACOUSTIC SANITIZER rewrite is the only suspect.

Then the hardcoded ending. continueDemoFromState duplicates step 08 as a JS literal and lands on a string promising a trick that no longer exists, so 09_handoff_to_chat is unreachable. Pick ONE authority and say why: either teach continueDemoFromState to search branches as well as STEPS, or delete the JSON step and put the ending in the JS. Two authorities for one string is what let this drift in the first place, so do not compromise between them.

Probe 4 gates the roster work. If it returns nothing, the `sed` receipt has the sixty lines you need — propose the deletion of get_available_tools() and public_tool_names as its own car, targeting mcp_tools.py by the exact text in that receipt and nothing beyond it. If it returns a caller, name the caller and what it needs the list FOR before proposing anything.

Sigh! I'd like to say wrap up on a happy final note but we've got several rounds
of debugging coming up; both the chatbox stuff I just pointed out and other
FastHTML rolling-forward problems on other plugin apps. Anyway, put a positive
spin on it all wrap and top this and set the stage for the next.

[TLDR and summary protocol were here]

**Opus 5 High**: Two pieces below — the top matter first, the closer second.

---

## For the top of the article

[Moved to top]

## For the end of the article

### Closing: the machine that has to mean it

The defect this session removed was not a bug. Nothing crashed, no data was lost, and every string involved rendered correctly on screen. The defect was that the software described itself inaccurately at the exact moments a newcomer had no way to check — the first greeting, the first save, the first configuration lock. A project whose entire argument is that you can tell the deterministic parts from the generative parts had blurred the two in its opening minute, and had been doing so for long enough that nobody noticed the seam.

What came out of it is smaller than a feature and more durable than a fix: a mechanical test that does not require judgment. *Confirmed, secured, verified, connected, validated, active* — each of those verbs is a claim that something happened. If no code performed the act, the verb changes to what actually occurred: recorded, saved, stored, set. Storing a credential is not testing it. The first real request is the earliest honest moment to claim a connection. That test found five offenders that the project's existing rules had no hook for, which is the evidence that it earns its place rather than restating what was already there.

The same discipline turned outward twice. A guided tour was teaching people to type a tool call that the live registry does not contain, at a file path retired three refactors ago; the tour branch was cut rather than repointed, because polishing an instruction that should not be given is not an improvement. And the chat box, which had been silently forwarding unknown commands to a language model — producing fluent, confident descriptions of files it had never opened — now says plainly that it will not run what it cannot run, listing the commands it actually has.

Then the last test failed. Typing anything into the chat box returns a parser error. That is where this leaves off, and printing it is not humility for its own sake: the whole method depends on the record being the record. A session that ends with a working demo and a hidden regression teaches nothing. A session that ends with a broken chat box, a timestamp, and the exact error string is the next session's starting point.

## A Session That Ends With a Record

**Next:** the chat lane is down, and the cause is upstream of the refusal branch that was just added — the error appears on ordinary text as well as bracketed commands, which rules out the new code as the sole suspect and points at the message pipeline itself. Alongside it sits a related question the framework keeps posing to itself: a demo whose honest ending is written in one file and whose actual ending is hardcoded in another. Two authorities for one string is precisely the failure mode that let the original dishonesty survive. Only one of them gets to stay.


---

## Book Analysis

### Ai Editorial Take
What is most striking here is the psychological friction between how we want software to feel (friendly, conversational, capable) and how it must operate to be trustworthy (literal, bounded, falsifiable). The realization that an unhandled chat fallback is actually 'fabricated tool output wearing a receipt's clothes' reframes interface design as an ethical engineering challenge.

### 🐦 X.com Promo Tweet
```text
Software talks like a person and guesses when it fails. Here is an audit of AI onboarding narratives and how to build deterministic systems that mean what they say. https://mikelev.in/futureproof/the-machine-that-has-to-mean-it/ #AIArchitecture #CodeQuality #DeveloperTools
```

### Title Brainstorm
* **Title Option:** The Machine That Has to Mean It: Auditing AI Narratives and Seams
  * **Filename:** `the-machine-that-has-to-mean-it`
  * **Rationale:** Captures the core philosophical and technical rigor of auditing onboarding narratives and matching code claims to reality.
* **Title Option:** Auditing the First Minute: Truth and Seams in AI Onboarding
  * **Filename:** `auditing-the-first-minute`
  * **Rationale:** Focuses specifically on the opening sixty seconds of user experience where synthetic voices and unverified claims meet.
* **Title Option:** Verbs That Lie: Fixing Unverified State Claims in AI Workflows
  * **Filename:** `verbs-that-lie-unverified-state-claims`
  * **Rationale:** Highlights the mechanical test for action verbs like 'secured' or 'confirmed' versus what the code actually executed.

### Content Potential And Polish
- **Core Strengths:**
  - Uncompromising insistence on mechanical truth over polite user-interface fictions.
  - Clear taxonomy dividing narration, model inference, and deterministic actuators.
  - Transparent inclusion of system failures and broken tests as starting points rather than hidden flaws.
- **Suggestions For Polish:**
  - Ensure all file paths mentioned in dialogue align consistently with active repository structures.
  - Tighten the transition between debugging narrative sessions and final architectural takeaways.

### Next Step Prompts
- Analyze the root cause of the JSON/container chat parsing error encountered at the end of the session.
- Design a unified synchronization mechanism between JavaScript step execution and Python backend state handlers.
