The One-Door Threshold: Engineering Replayable AI Workflows and Quiet Shells

๐Ÿค– Read Raw Markdown โ€ข ๐Ÿ“„ Google Doc (Try: Tools/Audio/Listen to document summary)

Setting the Stage: Context for the Curious Book Reader

This essay explores an important pivot in local developer environments, examining how simplifying terminal entry points and aligning interface controls with verifiable test harnesses keeps human operators in control. By trading slick graphical abstractions and complex menu systems for lean command lists and replayable steps, development workflows become transparent, checkable, and completely resistant to silent drift in the Age of AI.

TL;DR: This article completes a simplification of Pipulateโ€™s first interactive shell experience. A numbered startup selector was replaced by a small command vocabulary, while the guided browser workflow was adjusted so visible instructions precede speech, human-guided capture avoids an automation-specific delay, and optional local audio cues remain outside the repository. The work was verified with compile-time receipts and source inspection, with one unresolved Nix-parser environment issue recorded rather than treated as a successful parse.


Technical Journal Entry Begins

๐Ÿ”— Verified Pipulate Commits:

MikeLev.in: What surprised the Gemini AI Article Editor about the last article was:

What surprised me most about this entry is how it reframes the friction of the terminal not as an obstacle to be smoothed away by slick graphical interfaces, but as the exact mechanical governor that guarantees intellectual honesty. By forcing every interaction through a human-paced review loop, the system transforms chaotic chat sessions into a clean, verifiable audit trail.

Correct! Itโ€™s funny thatโ€™s surprising to an AI and pleasing that it notices.

A couple more things Iโ€™m noticing about the publishing pipeline as I release that last article is that the blog โ€œNextโ€ arrows layered in for the Jekyll HTML pages are missing. The โ€œPreviousโ€ article arrows are fine but the next is missing and thatโ€™s not only because Iโ€™m looking at the latest article that doesnโ€™t have a next. If I click previous either from the localhost Jekyll live-preview site or from the actual live-on-the-Web version, the previous arrows are missing but thatโ€™s a to-do to earmark and not for this article. The other thing is that the Google Doc link is added only after the actual git push release meaning thereโ€™s a lag for the Google Doc links on each article published until the article after. It shows on the Jekyll live-preview site on localhost after a publish "Message" event but not on the live Web site. Also an earmark not for this article, but be sure to record it.

Okay so for the continuation of the last article we already even have our probes and so we can get out ahead of it setting the stage helping the LLM help us for the first (costly) Fable 5.1 turn to be as effective as we can make it.

$ git status
On branch main
Your branch is up to date with 'origin/main'.

nothing to commit, working tree clean
(nix) pipulate $ rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-100
rg -n 'ADHOC_CHOP|^PINNED_CHOP = r' flake.nix foo_files.py prompt_foo.py | cut -c1-100
rg -n 'staleness_of|Proceeding anyway|_start_scrape_music|aplay' tools/scraper_tools.py | cut -c1-100
rg -n 'def _narrate|def narrate|speak_text' scripts/mother_cat.py | cut -c1-100
rg -c -w sources flake.nix scripts/boot_menu.py scripts/sources_menu.py assets/installer/mck.sh
curl -fsSL https://pipulate.com/mck.sh | wc -l; wc -l < assets/installer/mck.sh
scripts/boot_menu.py:6:  [1] JupyterLab tab     both servers start; JupyterLab opens in the browser 
scripts/boot_menu.py:8:  [3] Pipulate tab       both servers start; the app opens in the browser ins
scripts/boot_menu.py:164:        "[1]  JupyterLab tab     both servers start; JupyterLab opens in th
scripts/boot_menu.py:166:        "[3]  Pipulate tab       both servers start; the app opens in the b
scripts/boot_menu.py:185:                title="nix develop -- a reproducible *nix shell :: pick a d
scripts/boot_menu.py:193:        print("--- nix develop -- a reproducible *nix shell :: pick a door 
foo_files.py:1486:# scripts/boot_menu.py        # <-- The three-door workshop menu; walk, epr and cp
flake.nix:1737:              "You are Yen Sid-ton, the onboarding wizard for Pipulate. A newcomer wa
foo_files.py:1971:ADHOC_CHOP = r"""
foo_files.py:1973:# COMMAND: python prompt_foo.py --chop ADHOC_CHOP --no-tree
foo_files.py:1990:PINNED_CHOP = r"""
foo_files.py:2942:# - Add a fu-style toggle for `ahc`: a flag or sibling alias (e.g. `ahf`) that com
flake.nix:1516:          ahc() { (cd "$PIPULATE_ROOT" && python prompt_foo.py --chop ADHOC_CHOP --no
flake.nix:1517:          ahcu() { (cd "$PIPULATE_ROOT" && python prompt_foo.py --chop ADHOC_CHOP "$@
flake.nix:1529:          # breaks every ADHOC_CHOP compile in that shell, ahc and sniff and
flake.nix:1564:          cpr() { (cd "$PIPULATE_ROOT" && PIPULATE_ADHOC_FILE="$(_walkrouter)" python
77:# never touch any other aplay pipeline. The wav lives in repo negative space
94:def _start_scrape_music(verbose=True):
102:            ["sh", "-c", f'while :; do aplay -q -D default "{music}"; done # {SCRAPE_MUSIC_MARKE
569:        music_proc = _start_scrape_music(verbose=verbose)
614:            WebDriverWait(driver, 20).until(EC.staleness_of(initial_body))
621:            if verbose: logger.info(f"Did not detect a page reload for security challenge. Proce
756:            # staleness_of() wait above exists because a security challenge
50:def _narrate(text, disclosed, indent="  "):
70:    # message. When the per-user lock fix made speak_text stop failing, the
78:            result = chip_voice_system.speak_text(
93:        result = chip_voice_system.speak_text(text)
scripts/sources_menu.py:2
scripts/boot_menu.py:3
flake.nix:5
618
618
(nix) pipulate $

And we also have the context to set. Iโ€™m going to start a new discussion for this just to clear the slate and continuously demonstrate itโ€™s not about any of your existing in-chat-framework discussions from the vendors thatโ€™s important. I could just as easily be starting out fresh on ChatGPT and carrying over all this context. We do that by โ€œediting inโ€ the big chunk from our Ad Hoc Chop file. Iโ€™m going to leave all the context-turns from the last article in that file but commented inactive. Iโ€™ll add the context left dangling from the last article and so together with the probe step above we have a whole straddled reading but I donโ€™t think thereโ€™s an actual mutation I did so itโ€™s just strongly setting initial and quite big context.

Oh yeah and also to really just solidify context, I throw in the whole previous article.

(nix) pipulate $ posts 1
# ๐ŸŽฏ Target: MikeLev.in (Public) [Oldest First]

# fm cache: 1482 hits, 1 misses
/home/mike/repos/trimnoir/_posts/2026-09-18-the-first-ten-minutes-reproducible-ai-workflows.md  # [Idx: 1 | Order: 2 | Tokens: 69,595 | Bytes: 292,189]
(nix) pipulate $

So the context looks like like this:

# AD HOC CHOP! The Not-Managed-by-Git Safe-for-Client-Data place. Insert Simpson Couch Gag in white space  below (explain anything to the audience you feel needs it explained)G
# adhoc.txt    _   _   _               ____ _   _  ___  ____  _   
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  A nap has been had. I can think about this next step.
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  

# To Human: WELCOME TO VIM! It's really NeoVim but I say vim anyway.
# To AI: This is an alternate 40K view to the big book-ore rolling pin spine.

# 1. **Probe**: Baseline Reading
# 2. **Context**: Post-experiment *planned* reading instructions
# 3. **Patch**: The experiment and how to make it happen
# 4. **Prompt**: Post-experiment instructions and how to read results
# 5. **Deliverable**: How the world is forever different moving forward

# The first thing you need to know here is that everything that comes after the
# hash symbol (#) is commented out โ€” and that's EVERYTHING in this file's default
# state. Begin editing-in lines for inclusion as part of the context or adding
# chunks of new context at the bottom. `Ctrl`+`v`, `j` (repeatedly), `l` (to move
# right), `d` (to delete). Reverse that with `Ctrl`+`v`, `j` (repeatedly),
# `Shift`+`i`, `# `, `Esc` to put the hashes back. You can just arrow-key around
# here with `h`, `j`, `k`, `l`. Save-and-quit is a bit tricky because another
# file is also loaded: `Esc`, `:`, `q`, `w`, `!`

# If this is stressing you out and you're a quitter and want to quit, just type:
# `Esc`, `:`, `q`, `!`, `Enter`. That will exit without saving any changes. If
# you want to get over this hump, type: `Esc`, `:`, `T`, `u`, `t`, `o`, `r`, `Enter`.

# This file is just to make it easy having options of what to edit into context.
# You can use whatever text-file you want to stack file-names and commands to
# build an output text-file with the identically stacked output of each file or
# command. In this way we vertically append or "stack" a bunch of text; simple as
# that. If you understand this concept, you're on your way to future-proofing
# yourself in the Age of AI. Congratulations! Here is how to include web pages:

#    !URL  --------------------------------------------------------------------
#      when    Public page; what a stranger or crawler sees; the BEFORE of a
#              login-wall diagnosis
#      switch  It shows a login page -> `warm URL` once, then `?URL`
#    
#    ?URL  --------------------------------------------------------------------
#      when    Anything behind a login, on the site's persistent profile;
#              `check URL` first
#      switch  The lenses show a shell (nav, an `[Iframe]` leaf, no content) ->
#              read the wire truth for the XHR the frame makes, then call that
#              API with a connector
#    
#    @URL  --------------------------------------------------------------------
#      when    Every re-read of a page already scraped; no browser, no network
#      switch  The cached page is stale or was a login wall -> fresh `!` or `?`
#    
#    $URL  --------------------------------------------------------------------
#      when    Exact markup: meta tags, a JSON blob in a `<script>`
#      note    Token-heavy; needs a prior scrape
#    
#    %URL  --------------------------------------------------------------------
#      when    The network log distilled; SPA endpoint discovery
#      switch  It re-serves the wire truth you already have -> the API
#    
#    ! cmd  -------------------------------------------------------------------
#      when    Any bounded, non-interactive command as a live receipt
#      note    Cap it with `-n`; no aliases, no prompts
#    
#    Connector  ---------------------------------------------------------------
#      when    The number you want is one GET away
#      switch  LIST until the thing isn't in the list -> FETCH by id -> DRILL
#              the path the app's own frame called -> `--grep` to narrow a list
#              or find a leaf

# Every step is one argument longer than the last; the moment a lens shows less than the wire does is the moment to stop scraping.

# FOR 40K-FT VIEW (STORY & INFRASTRUCTURE) !!
# --- START EDITING-IN ON 1ST TURN ---

! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs  # <-- ROLLING PIN that gives the 40K foot book-spine view of book-ore (only works for me because of local-only git repo)
~/repos/nixos/autognome.py  # <-- You wake up in the morning and your Tooling & Instrumentation folds out of you like Inspector Gadget.
init.lua                    # <-- Those gadgets are made easy-to-use through nifty keyboard shortcuts (but ya gotta learn vim).
GLOSSARY.md                 # <-- Like the back of a J.R.R. Tolkien book, there's kooky new terms to know.
flake.nix                   # <-- Here is my hardware. Here is my state. Put on your sandbox. And please recreate. (Infrastructure as Code / IaC)
prompt_foo.py               # <-- THIS system
foo_files.py                # <-- main ROUTER
requirements.in             # <-- We've "pinned" everything but still want a flexible Python Data Science virtualenv.
pyproject.toml              # <-- How this is a citizen of the Python "pip install" ecosystem
__init__.py                 # <-- Version info

# --- END EDITING-IN ON 1ST TURN ---

# scripts/articles/lsa.py     # <-- 2ND BRAIN: Search external memory with `rgx`, `rgxc` & `posts` Blogging for Hackers Jekyll-compatible.

# OPTIONAL ACTUATORS (cheap and good to include to expand the AI's capabilities)
# 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.
# scripts/crawl.py            # <-- Feel free to ask for something to be crawled and included in the next turn.
# scripts/weblogin.py         # <-- Lets the user "warm up" the cache for their web logins at their leisure on a profile that persists.
# scripts/webclip_2_markdown.py  # <-- Surprisingly important program.
 
# MISCELLANEOUS (rare to include but sometimes critical)
# scripts/foo_cartridge.py    # Needs description
# scripts/foo_replay.py       # Needs description
# release.py                  # <-- How everything ends up where it does (GitHub, PyPI, etc.)
# imports/voice_synthesis.py  # <-- The wand can talk to you
# imports/ascii_displays.py   # <-- Where all the ASCII Art lives
# 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.  ---

# Carry-over as the important work-in-progress parts of the project here just
# like above but not as long-standing overarching to the framework but rather
# for the current hot spots actively being worked on.

# --- START THIS DISCUSSION ---

# Get things started here! Guess at what context should be included.
# If you get it wrong, you're just wasting 1-turn because the AI will help.
# Un-comment lines, add lines with absolute-path filenames or `! ` commands. 

# Context 1 
# /home/mike/repos/trimnoir/_posts/2026-09-18-the-first-ten-minutes-reproducible-ai-workflows.md

# Context 2
# ! git status --short
# ! rg -n 'DEFAULT_MODEL' scripts/articles/articleizer.py | cut -c1-100
# ! git log --since='7 days ago' --format= --name-only | sort | uniq -c | sort -rn | head -n 8
# ! rg -c 'scripts/connectors' "$HOME/.local/state/pipulate/adhoc.txt"
# ! LD_LIBRARY_PATH="" nix --version | tail -n 1; git --version; python --version; nvim --version | head -n 1
# foo_files.py
# scripts/boot_menu.py
# assets/installer/mck.sh

# Context 3
# ! rg -n 'NEW terminal|new terminal|re-run' assets/installer/install.sh | cut -c1-100
# ! rg -n 'words to start from|Nothing started|pick a door' scripts/boot_menu.py | cut -c1-100
# ! rg -n 'Trail resolved|Choose a walk|CAPTURE RUN FINISHED' assets/installer/mck.sh | cut -c1-100
# ! rg -n 'ROUTER LOADED|copied to clipboard|Deed:' prompt_foo.py | cut -c1-100
# ! rg -n 'intro-contract|voice' scripts/mother_cat.py | head -n 8 | cut -c1-100
# ! ls -la ~/.config/pipulate/voice ~/.local/state/pipulate/adhocwalk.txt
# flake.nix
# assets/installer/install.sh
# scripts/boot_menu.py
# assets/installer/mck.sh

# Context 4
# ! rg -n 'boot_menu' assets/installer/mck.sh | cut -c1-100
# ! curl -fsSL https://npvg.org/mck/public_walk | wc -l; curl -fsSL https://pipulate.com/mck.sh | wc -l; wc -l < assets/installer/mck.sh
# ! rg -n 'Close this terminal|bash -s' assets/installer/install.sh | cut -c1-100
# ! rg -n 'local_dir_use_symlinks|getLogger|disable_progress|HF_HUB' imports/voice_synthesis.py | cut -c1-100
# ! rg -n 'Auto-healing|version_main|readyState|conjure_window|network_log' tools/scraper_tools.py | cut -c1-100
# ! rg -n -i 'jeopardy' imports tools scripts | cut -c1-100; git ls-files '*.wav' | head -n 5
# ! rg -n 'intro-contract|intro_contract|Opening the browser|Summary file' scripts/mother_cat.py | cut -c1-100
# imports/voice_synthesis.py
# tools/scraper_tools.py

# Context 5
# ! rg -n 'local_dir_use_symlinks|getLogger|disable_progress|catch_warnings' imports/voice_synthesis.py | cut -c1-100
# ! rg -n 'Auto-healing|version_main|_HEAL' tools/scraper_tools.py | cut -c1-100
# ! .venv/bin/python -c 'import imports.voice_synthesis as v, tools.scraper_tools as s; print(v.VOICE_SYNTHESIS_AVAILABLE, s._HEAL)'
# ! rg -c 'Checkword' data/decant-preview.md
# ! rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-100
# ! rg -n 'ADHOC_CHOP|^PINNED_CHOP = r' flake.nix foo_files.py prompt_foo.py | cut -c1-100
# ! sed -n '/^ADHOC_CHOP = r/,/^"""/p' foo_files.py
# ! curl -fsSL https://pipulate.com/mck.sh | wc -l; wc -l < assets/installer/mck.sh
# scripts/boot_menu.py
# flake.nix

# Context 1
/home/mike/repos/trimnoir/_posts/2026-09-18-the-first-ten-minutes-reproducible-ai-workflows.md
! rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-100
! rg -n 'ADHOC_CHOP|^PINNED_CHOP = r' flake.nix foo_files.py prompt_foo.py | cut -c1-100
! rg -n 'staleness_of|Proceeding anyway|_start_scrape_music|aplay' tools/scraper_tools.py | cut -c1-100
! rg -n 'def _narrate|def narrate|speak_text' scripts/mother_cat.py | cut -c1-100
! rg -c -w sources flake.nix scripts/boot_menu.py scripts/sources_menu.py assets/installer/mck.sh
! curl -fsSL https://pipulate.com/mck.sh | wc -l; wc -l < assets/installer/mck.sh
scripts/boot_menu.py
scripts/mother_cat.py
tools/scraper_tools.py

# --- END `adhoc.txt` TEMPLATE ---

And now when I use the ahc command, the terminal output looks like this which can give you an idea of the prompt sizes involved:

(nix) pipulate $ ahe
(nix) pipulate $ prompt
(nix) pipulate $ ahc
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ๐Ÿฐ ASCII Art Wax Seal (your vibe-coding safety-net) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                                                                                                                                                   โ”‚
โ”‚                         ( Like a canary you say? )                                                                                                                                โ”‚
โ”‚                                            O        /)  ____            The "No Problem" Framework                                                                                โ”‚
โ”‚ >  I HEREBY WILL NOT RE-GENERATE            o /)\__//  /    \        Pipulate - Protecting Your Code                                                                              โ”‚
โ”‚ >  Once upon machines be smarten          ___(/_ 0 0  |      |       just by being honest about text.                                                                             โ”‚
โ”‚ >  ASCII sealing immutata art in        *(    ==(_T_)== NPvg |        (If mangled, then AI drifted.)                                                                              โ”‚
โ”‚ >  This here cony if it's broken          \  )   ""\  |      |             https://pipulate.com                                                                                   โ”‚
โ”‚ >  Smokin gun drift now in token           |__>-\_>_>  \____/                     ๐Ÿฅ•๐Ÿฅ•๐Ÿฅ•                                                                                          โ”‚
โ”‚                                                                                                                                                                                   โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
๐Ÿ—บ๏ธ  Codex Mapping Coverage: 74.7% (204/273 tracked files) (was 204/273: +0 claimed, +0 tracked).
ROUTER LOADED: /home/mike/.local/state/pipulate/adhoc.txt (20 active line(s))

โœ… Topological Integrity Verified: 29 candidate reference(s) scanned, all exist.
   -> Executing: python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs ... [0.3035s]
   -> Executing: rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-100 ... [0.0204s]
   -> Executing: rg -n 'ADHOC_CHOP|^PINNED_CHOP = r' flake.nix foo_files.py prompt_foo.py | cut -c1-100 ... [0.0168s]
   -> Executing: rg -n 'staleness_of|Proceeding anyway|_start_scrape_music|aplay' tools/scraper_tools.py | cut -c1-100 ... [0.0112s]
   -> Executing: rg -n 'def _narrate|def narrate|speak_text' scripts/mother_cat.py | cut -c1-100 ... [0.0104s]
   -> Executing: rg -c -w sources flake.nix scripts/boot_menu.py scripts/sources_menu.py assets/installer/mck.sh ... [0.0134s]
   -> Executing: curl -fsSL https://pipulate.com/mck.sh | wc -l; wc -l < assets/installer/mck.sh ... [0.0775s]
   -> Ruff exit 0 (clean).
                                                                      ๐Ÿ“ฆ Payload Ledger (biggest first)                                                                       
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“
โ”ƒ File / Source                                                                                                                              โ”ƒ  Tokens โ”ƒ     Bytes โ”ƒ % Bytes โ”ƒ
โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ
โ”‚ foo_files.py                                                                                                                               โ”‚  83,860 โ”‚   335,493 โ”‚   21.4% โ”‚
โ”‚ /home/mike/repos/trimnoir/_posts/2026-09-18-the-first-ten-minutes-reproducible-ai-workflows.md                                             โ”‚  69,595 โ”‚   292,189 โ”‚   18.7% โ”‚
โ”‚ prompt_foo.py                                                                                                                              โ”‚  47,333 โ”‚   208,485 โ”‚   13.3% โ”‚
โ”‚ ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs                                                                          โ”‚  63,350 โ”‚   164,944 โ”‚   10.5% โ”‚
โ”‚ flake.nix                                                                                                                                  โ”‚  33,586 โ”‚   139,092 โ”‚    8.9% โ”‚
โ”‚ GLOSSARY.md                                                                                                                                โ”‚  30,878 โ”‚   123,617 โ”‚    7.9% โ”‚
โ”‚ scripts/mother_cat.py                                                                                                                      โ”‚  14,234 โ”‚    62,807 โ”‚    4.0% โ”‚
โ”‚ tools/scraper_tools.py                                                                                                                     โ”‚  10,929 โ”‚    50,283 โ”‚    3.2% โ”‚
โ”‚ /home/mike/repos/nixos/autognome.py                                                                                                        โ”‚  10,581 โ”‚    47,651 โ”‚    3.0% โ”‚
โ”‚ init.lua                                                                                                                                   โ”‚  10,402 โ”‚    39,825 โ”‚    2.5% โ”‚
โ”‚ apply.py                                                                                                                                   โ”‚   8,584 โ”‚    36,636 โ”‚    2.3% โ”‚
โ”‚ PROMPT (checklist + prompt.md)                                                                                                             โ”‚   7,234 โ”‚    28,467 โ”‚    1.8% โ”‚
โ”‚ scripts/boot_menu.py                                                                                                                       โ”‚   3,362 โ”‚    13,270 โ”‚    0.8% โ”‚
โ”‚ AUTO: Recent Git Diff Telemetry                                                                                                            โ”‚   2,264 โ”‚     8,589 โ”‚    0.5% โ”‚
โ”‚ pyproject.toml                                                                                                                             โ”‚   1,129 โ”‚     4,104 โ”‚    0.3% โ”‚
โ”‚ .gitignore                                                                                                                                 โ”‚     970 โ”‚     3,598 โ”‚    0.2% โ”‚
โ”‚ __init__.py                                                                                                                                โ”‚     699 โ”‚     2,888 โ”‚    0.2% โ”‚
โ”‚ requirements.in                                                                                                                            โ”‚     675 โ”‚     2,340 โ”‚    0.1% โ”‚
โ”‚ ! rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-100 โ”‚     220 โ”‚       807 โ”‚    0.1% โ”‚
โ”‚ ! rg -n 'ADHOC_CHOP|^PINNED_CHOP = r' flake.nix foo_files.py prompt_foo.py | cut -c1-100                                                   โ”‚     228 โ”‚       646 โ”‚    0.0% โ”‚
โ”‚ ! rg -n 'staleness_of|Proceeding anyway|_start_scrape_music|aplay' tools/scraper_tools.py | cut -c1-100                                    โ”‚     140 โ”‚       544 โ”‚    0.0% โ”‚
โ”‚ ! rg -n 'def _narrate|def narrate|speak_text' scripts/mother_cat.py | cut -c1-100                                                          โ”‚      60 โ”‚       234 โ”‚    0.0% โ”‚
โ”‚ .gitattributes                                                                                                                             โ”‚      33 โ”‚        76 โ”‚    0.0% โ”‚
โ”‚ ! rg -c -w sources flake.nix scripts/boot_menu.py scripts/sources_menu.py assets/installer/mck.sh                                          โ”‚      21 โ”‚        60 โ”‚    0.0% โ”‚
โ”‚ AUTO: Static Analysis Diagnostics                                                                                                          โ”‚      11 โ”‚        39 โ”‚    0.0% โ”‚
โ”‚ ! curl -fsSL https://pipulate.com/mck.sh | wc -l; wc -l < assets/installer/mck.sh                                                          โ”‚       3 โ”‚         7 โ”‚    0.0% โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ TOTAL                                                                                                                                      โ”‚ 400,381 โ”‚ 1,566,691 โ”‚  100.0% โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
**Command:** `prompt_foo.py --chop ADHOC_CHOP --no-tree`

--- Auto-Context Metadata ---
โ€ข Static Analysis Diagnostics (11 tokens | 39 bytes)
โ€ข Recent Git Diff Telemetry (2,264 tokens | 8,589 bytes)

--- Prompt Summary ---
Summed Tokens:    403,303 (from section parts)
Verified Tokens: 406,686 (from final output)
  (Difference: +3,383)
Total Words:      197,615 (content only)
Total Chars:      1,575,293
Total Bytes:      1,586,233 (UTF-8)

--- Size Perspective ---
๐Ÿ“š Equivalent in length to a **Epic Fantasy Novel** (Note: With a token/word ratio of 2.06, this content is far denser and more complex than typical prose of this length).
๐Ÿงผ Identity scrub: substitutions=57 rule(s) | denylist=0 rule(s)
๐Ÿ” Secrets tripwire: ARMED โ€” 0 hit(s) in payload.
๐Ÿ“ฆ Canonical context cartridge written to /home/mike/repos/pipulate/foo.zip (sha256=9b5c9703519dโ€ฆ, members=3)
๐Ÿ—‚๏ธ  Rotated cartridge snapshot: foo-9b5c9703-1484.zip (keeping newest 20 of 20)
Markdown output copied to clipboard
(nix) pipulate $

Thatโ€™s about 1.5 megabytes by the way. Thatโ€™s the size of an old-school mini floppy disk by the way, what those โ€œSaveโ€ icons are a picture of.

Note: While thatโ€™s thinking time for some thoughts. We pay machines to think for us now covered by the โ€œgym membershipโ€ subscription model that masks actual token costs. Thatโ€™s not in some abstract or theoretical way and not with some unmeasurable masking. We literally pay machines to think for us by the token and once you go over your allocated allowed gym-time from your subscription, you can start measuring that cost precisely. The run before this had my token-spend at $103.61. After the thinking it just did the meter is atโ€ฆ

WHOOPS!

I just got the Fable 5.1 ACME Anvil-of-crushed dreams:

Thought for 8m 8s

Reviewing a new article prompt to understand the request.

Parsing the detailed spec for the next article's structure.

Scoping which pieces to tackle in the first turn.

Planning the boot menu overhaul and its door logic.

Reconciling menu word lists against the operator's ruled draft.

Deciding whether to keep or drop an existing menu word.

Tracing every occurrence of a renamed function across the flake.

Mapping out a full rewrite of the menu file.

Weighing whole-file rewrite risk against surgical edits.

Threading a new shell function into the existing script flow.

Sweeping the codebase for leftover references to the old menu.

Predicting output changes when a helper function is quieted.

Drafting the compiled function's success-only output lines.

Verifying the slot syntax matches the parser's expected pattern.

Updating flake comments to match the new door logic.

Adjusting the spoken greeting for the new restart path.

Deciding how much of the rider scope fits this turn.

Trimming a duplicate consent notice down to one print.

Designing a narration hook for the checkpoint capture step.

Adjusting narration timing and spacing around the capture prompt.

Updating docstring wording to match the indent change.

Sizing this turn's scope across several planned edits.

Weighing minimal timeout tweaks against a riskier block rewrite.

Finalizing the shortened wait with an explanatory comment.

Choosing a platform-aware audio player for the wait sound.

Sequencing the rider edits into ordered dependent commits.

Reordering commits so no edit lands before its dependency.

Drafting the boot menu's new fail-open docstring.

Drafting the short and expanded menu word lists.

Simplifying the printer helper and weighing name-formatting removal.

Wiring the recall path's exit codes and tty checks.

Documenting the fail-open design and adding new shell helpers.

Confirming shared scope for a new shell function's variable.

Updating flake comments to describe the new startup behavior.

Rewriting spoken prompts and comments to match the new flow.

Rewriting the compiled function's output to avoid false claims.

Renaming a menu command and updating its comment table.

Predicting leftover keyword hits after the rename sweep.

Drafting the compiled router block and its comment lines.

Counting inserted lines to predict the resulting slot offset.

Drafting new TODO entries about publish-timing bugs.

Confirming the publish-order bug's root cause from source.

Scripting a narration hook for the browser capture prompt.

Threading the checkpoint callback through the capture signature.

Rewriting docstring wording to explain the trimmed notice.

Choosing a platform-aware audio command for background music.

Locating candidate music files to match the wait sound.

Paused

Edit and retry with Fable 5.1

Fable 5.1โ€™s safeguards flagged this message. Our intentionally broad safeguards allow us to deliver more capabilities faster, but can sometimes flag legitimate cybersecurity work. Apply to the Cyber Verification Program to reduce these interruptions.ย Continue with Opus 4.8,ย send feedback,ย apply for verification, orย learn more.

Details:ย [cyber]

And I an NOT going to Opus 5 as a fallback because Opus 5 takes too many turns and when itโ€™s done I canโ€™t understand what it says. So over to ChatGPT 6 for this article. Whoops Anthropic! Iโ€™ll same myself some money because my premium ChatGPT usage is all under a work account and not quite so stingy as they are with Claude which is also are official one to use. Go figure. If you want to be really effective in the Age of AI you canโ€™t let yourself be limited by your employer. In fact thatโ€™s just a good lesson in life. Almost everyone in your life doles out resources is going to slightly under-nourish you either because of their own lack of foresight and insight, or by design. Itโ€™s a gardening thing; no one thing can be allowed to overgrow a garden so it always gets snipped and pruned and hedged. Itโ€™s not the wrong way to tend a garden. Just know when weeding is occurring to you or around you. Itโ€™s okay for plants in a garden to get rained on. Just be aware of the rain and adapt accordingly.

ChatGPT 6 also gets the benefit of all of Fable 5.1 thought-process headlines before Anthropic drove the business to them.

Note: Okay, ChatGPT is thinking and while it does I do too. Criticism is mounting for people who submit a prompt and then wait what the complaints are quantifying at 2 to 5 minutes but for my prompts itโ€™s not unusual to get up to 10 minutes or more. I donโ€™t sit and watch it think (sometimes I do) but mostly I anticipate and write-ahead just like this. Of course this part the model hasnโ€™t read you by the time you see the response Iโ€™ll be printing below soon, but thatโ€™s fine. It will see all this the turn after that. Sometimes I do put it down there in the article rather than in a โ€œNoteโ€ section above. Also sometimes I make this note block a blockquote to make it stand out, but lately Iโ€™ve been avoiding those greater-than symbols for blockquotes unless they really need to be because I think they interrupt otherwise contiguous blocks of text for the sake of model-training. Blockquotes probably require some special handling for sentences to be sentences without strange token interruptions sprinkled in. Of course I could turn this entire thing into a single line with only one greater-than symbol for the whole blockquote too. Okay, let me do that. I do a little of this and a little of that. Not very scientific but over time Iโ€™ll accumulate empirical evidence of such things. Thatโ€™s regular SEO-work. Not nearly as scientific as it could be.

Is it still going? Yeah, still going. Okay I am watching it think a bit. Itโ€™s absolutely fascinating seeing it get up to the point of coming to grips that confabulation and those confident hallucinations are not going to fly here. Even ambitious attempts to โ€œfill in missing parts with frog DNAโ€ technically canโ€™t work with my system because of the Aider-style exact SEARCH/REPLACE interlock. Itโ€™s like springing the surprise on these LLMs something like: โ€œI donโ€™t care whatโ€™s worked for you in the past. If youโ€™re not creating your mutation-patches with the actual source material youโ€™re editing โ€œin-mindโ€ sufficiently that you canโ€™t reproduce it exactly byte-for-byte, line-returns, whitespace and weird characters like em-dashes and all, donโ€™t even try; you will look like a stumbling idiot because none of those patches will land.

This realization was quite an awakening and a source of a lot of my mechanical sympathy when it comes to how these Models work. You see sentences like โ€œIโ€™m testing those fences with the repoโ€™s own apply.py parser now, not merely eyeballing them.โ€ which is really quite satisfying and more evidence of their own sandbox virtual machine or something of that sort. Knowing it might be Nix or containers back there powering that you canโ€™t really assume virtual machines. Sandbox is probably the right umbrella term. These LLMs are much more likely to have able-to-quickly-reproduce-your-system sandboxes available to theme these days to reduce guesswork exactly like it is in this example. Oh now that I have had this awakening I think I can delete the old AI_RUNME.py file; itโ€™s purpose has been served. But I donโ€™t want to just delete it because of all the references, but itโ€™s no longer needed I think. Earmark or to-do that please ChatGPT. Itโ€™s not for this article but for noting it.

ChatGPT 6 Extra High: Context verified.

The cartridge explicitly makes the final Prompt authoritative and asks for apply.py-style SEARCH/REPLACE edits. One limitation in the compile itself: the File Tree section was skipped, so I could not perform a literal tree-vs-source comparison from that section. The mutation surfaces required for this ride are nevertheless present as raw source, and none of the unchanged local imports needs an interface change.

I also changed one detail from the tempting implementation: cpr says โ€œDelivery lane:โ€, not โ€œWrote:โ€. prompt_foo.py explicitly does not return a trustworthy clipboard-delivery status, so claiming โ€œWroteโ€ would manufacture a success receipt. brief survives in all; wake stays absent because no mechanism has been ruled for it; and the walkโ€™s own Practice/Start/Exit 2 remains untouched.

I ran all six cars sequentially through the supplied apply.py against clean copies. Every exact-match patch applied, final files byte-matched the intended versions, the four Python files passed py_compile, the short recall prints six words, expanded recall prints nine, PIPULATE_BOOT_MENU=0 and non-TTY entry both return 0, and a pseudo-TTY prints the list and returns 10. This sandbox has no Nix installation, so for that local validation I stubbed only nix-instantiate to exercise apply.pyโ€™s other interlocks; that is not a Nix syntax proof. The real Nix parse is therefore deliberately first-class in the probes below.

Engineering Fail-Open Shell Mechanics

  1. PROBES

These are read-only and paste-ready:

.venv/bin/python -m py_compile scripts/boot_menu.py scripts/mother_cat.py tools/scraper_tools.py foo_files.py
.venv/bin/python scripts/boot_menu.py --recall
.venv/bin/python scripts/boot_menu.py --recall --all
PIPULATE_BOOT_MENU=0 .venv/bin/python scripts/boot_menu.py; printf 'env_rc=%s\n' "$?"
printf '' | .venv/bin/python scripts/boot_menu.py; printf 'pipe_rc=%s\n' "$?"
rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-120
rg -n 'sources\(\)|echo "sources:|DOOR_TWO_WORDS|three-door workshop menu|door 2 opens onto|sources panel' flake.nix foo_files.py scripts/boot_menu.py
rg -n 'WALK_CHOP|--chop WALK_CHOP|Read: \$router|Delivery lane:' flake.nix foo_files.py prompt_foo.py | cut -c1-140
rg -n 'before_prompt|staleness_of|tick\.wav|ding\.wav|afplay|aplay' tools/scraper_tools.py scripts/mother_cat.py | cut -c1-140
rg -n 'def _narrate|Make sure your audio is turned on|The page is open|INTRO_NOTICE' scripts/mother_cat.py | cut -c1-140
nix-instantiate --parse flake.nix >/dev/null
  1. NEXT CONTEXT

Exact adhoc.txt content. The probe lines above are repeated verbatim with only the required leading ! :

flake.nix
foo_files.py
prompt_foo.py
scripts/boot_menu.py
scripts/mother_cat.py
tools/scraper_tools.py
scripts/sources_menu.py
apply.py
! .venv/bin/python -m py_compile scripts/boot_menu.py scripts/mother_cat.py tools/scraper_tools.py foo_files.py
! .venv/bin/python scripts/boot_menu.py --recall
! .venv/bin/python scripts/boot_menu.py --recall --all
! PIPULATE_BOOT_MENU=0 .venv/bin/python scripts/boot_menu.py; printf 'env_rc=%s\n' "$?"
! printf '' | .venv/bin/python scripts/boot_menu.py; printf 'pipe_rc=%s\n' "$?"
! rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-120
! rg -n 'sources\(\)|echo "sources:|DOOR_TWO_WORDS|three-door workshop menu|door 2 opens onto|sources panel' flake.nix foo_files.py scripts/boot_menu.py
! rg -n 'WALK_CHOP|--chop WALK_CHOP|Read: \$router|Delivery lane:' flake.nix foo_files.py prompt_foo.py | cut -c1-140
! rg -n 'before_prompt|staleness_of|tick\.wav|ding\.wav|afplay|aplay' tools/scraper_tools.py scripts/mother_cat.py | cut -c1-140
! rg -n 'def _narrate|Make sure your audio is turned on|The page is open|INTRO_NOTICE' scripts/mother_cat.py | cut -c1-140
! nix-instantiate --parse flake.nix >/dev/null
  1. PATCHES

Car 1 โ€” THE ONE DOOR + conn/all/jn + WALK_CHOP/cpr

Target: scripts/boot_menu.py
[[[WRITE_FILE]]]
#!/usr/bin/env python3
"""
boot_menu.py โ€” the one-door threshold at the end of `nix develop`.

An interactive terminal no longer asks the newcomer to choose a numbered door.
It prints the short command list and returns 10, which tells flake.nix to stop
before either server starts and hand the human the shell prompt. `menu` recalls
the short list; `all` recalls the expanded list.

THE PROTOCOL IS THE EXIT CODE, never stdout:

  0   start the app       (non-tty or PIPULATE_BOOT_MENU=0)
  10  drop to the Nix CLI after printing the short list

FAIL-OPEN BY CONSTRUCTION. Every unattended path keeps the pre-menu behavior:
no tty, PIPULATE_BOOT_MENU=0, or an unexpected display failure returns 0. The
interactive path blocks on nothing and reads no keys; the terminal itself is
the introduction.

Stdlib only.

Env:
  PIPULATE_BOOT_MENU=0   skip the list and start the app
"""
import os
import sys

EXIT_START = 0
EXIT_SHELL = 10

SHORT_WORDS = (
    ("menu", "print this list again (useful once it scrolls away)"),
    ("walk", "take guided tour of context compiler (recommended)"),
    ("conn", "get mcp, jira, email, docs, etc. into context"),
    ("epr", "edit prompt router (after the walk)"),
    ("cpr", "compile prompt router (paste results into chatbot)"),
    ("all", "expanded menu"),
)

ALL_WORDS = (
    ("menu", "print the shorter list"),
    ("walk", "take guided tour of context compiler (recommended)"),
    ("conn", "get mcp, jira, email, docs, etc. into context"),
    ("epr", "edit prompt router (after the walk)"),
    ("cpr", "compile prompt router (paste results into chatbot)"),
    ("all", "print this expanded list again"),
    ("brief", "compile this workshop into your clipboard for an AI"),
    ("jn", "start JupyterLab and Pipulate, JupyterLab first"),
    ("pu", "start the Pipulate server"),
)

def print_words(words) -> None:
    """Print one command list from data; short and expanded cannot drift."""
    print("type one:")
    width = max(len(word) for word, _ in words)
    for word, description in words:
        print("  " + word.ljust(width) + "  " + description)

def main() -> int:
    args = set(sys.argv[1:])

    # Recall is a display, not the startup threshold. It deliberately runs
    # before the unattended gates so `menu` and `all` also work when stdout is
    # redirected or PIPULATE_BOOT_MENU=0 is inherited by the shell.
    if "--recall" in args:
        print()
        print_words(ALL_WORDS if "--all" in args else SHORT_WORDS)
        return 0

    if os.environ.get("PIPULATE_BOOT_MENU", "1").strip().lower() in {
        "0",
        "no",
        "off",
        "false",
    }:
        return EXIT_START

    if not (sys.stdin.isatty() and sys.stdout.isatty()):
        return EXIT_START

    try:
        print()
        print_words(SHORT_WORDS)
    except Exception:
        return EXIT_START
    return EXIT_SHELL

if __name__ == "__main__":
    sys.exit(main())
[[[END_WRITE_FILE]]]
Target: flake.nix
[[[SEARCH]]]
          # THE BANNER MOVED TO DOOR 1 (2026-08-25). The figlet printed HERE,
          # ABOVE the threshold, so the human who was one keypress away from
          # saying "do not start the app" got a full-width ASCII assertion of
          # that app's identity first. The banner is the curtain going up on
          # the app; it belongs on the path where the app actually starts, and
          # it now renders just below the BOOT_CHOICE exit. PROPER_APP_NAME
          # stays computed here -- it is the figlet's only consumer and both
          # live in this one process, so the move strands nothing.
          #
[[[DIVIDER]]]
          # THE BANNER MOVED BELOW THE THRESHOLD (2026-08-25). The figlet
          # printed HERE, before the human had chosen whether to start anything,
          # so the shell-only path got a full-width assertion of the app first.
          # The banner is the curtain going up on the app; it belongs only on
          # the path where the app actually starts, just below the BOOT_CHOICE
          # exit. PROPER_APP_NAME
          # stays computed here -- it is the figlet's only consumer and both
          # live in this one process, so the move strands nothing.
          #
[[[REPLACE]]]
Target: flake.nix
[[[SEARCH]]]
          # OUTSIDE THE THRESHOLD BY DESIGN -- these three run on BOTH sides:
          #   copy_notebook_if_needed -- notebooks on disk are a deliverable of
          #     the flake, not of the server. Idempotent, cheap, and a door-2
          #     user who later runs `python server.py` should find them there.
          #   pkill -- entering `nix develop` has ALWAYS killed a running
          #     server. Door 2 keeps that promise instead of inventing a new
          #     one, so a later `python server.py` finds :5001 free.
          #   git pull -- the `dev` shell skips gitUpdateLogic entirely, so
          #     this bare pull is dev's ONLY update path. Inside the branch, a
          #     habitual door-2 user would silently drift from upstream and
          #     the magic cookie's forever-forward promise would rot.
[[[DIVIDER]]]
          # OUTSIDE THE THRESHOLD BY DESIGN -- these three run on BOTH paths:
          #   copy_notebook_if_needed -- notebooks on disk are a deliverable of
          #     the flake, not of the server. Idempotent, cheap, and a shell-only
          #     user who later runs `python server.py` should find them there.
          #   pkill -- entering `nix develop` has ALWAYS killed a running
          #     server. The shell-only path keeps that promise instead of
          #     inventing a new one, so a later `python server.py` finds :5001 free.
          #   git pull -- the `dev` shell skips gitUpdateLogic entirely, so
          #     this bare pull is dev's ONLY update path. Inside the branch, a
          #     habitual shell-only user would silently drift from upstream and
          #     the magic cookie's forever-forward promise would rot.
[[[REPLACE]]]
Target: flake.nix
[[[SEARCH]]]
          # THE THRESHOLD: two doors, asked BEFORE anything starts.
          #
          # boot_menu.py speaks ONLY through its exit code -- 0 start the
          # app, 10 stay in the shell. Nothing here parses its stdout, so no
          # capture pipe can ever be held open by it (the rgx/xclip deadlock
          # is the conviction). That also makes the renderer swappable: a
          # Textual boot_menu.py can replace the stdlib/Rich one and this
          # branch never learns the difference.
          #
          # FALL-THROUGH GUARANTEE: if scripts/boot_menu.py is absent -- an
          # older checkout, a partial clone, a hand-deleted file -- the flake
          # behaves EXACTLY as it did before the menu existed. The flake must
          # never depend on a file that might not have landed.
          BOOT_CHOICE=0
          if [ -f scripts/boot_menu.py ]; then
            python scripts/boot_menu.py
            BOOT_CHOICE=$?
          fi
          if [ "$BOOT_CHOICE" -eq 10 ]; then
            # DOOR 2 -- nothing starts. runScript is EXECUTED (named on its
            # own line in each shellHook), never sourced, so exiting here
            # ends this child process and drops the parent into its
            # interactive prompt. That is the same path door 2 already took
            # under the tail placement, witnessed 2026-07-23 landing at
            # `(nix) pipulate $`. Exiting BEFORE the JupyterLab launch is
            # what makes the quiet workshop quiet: no tmux session, no TTS
            # greeting, no 30-second readiness poll, and -- the visible bug
            # this fixes -- no backgrounded browser subshell spraying dots
            # over the prompt and then announcing that a server nobody
            # authorized failed to start.
            exit 0
          fi
          # DOOR 3 -- the Pipulate tab (2026-09-03). Same servers as door 1,
          # opposite tab. It assigns the two names the runtime tab shadows
          # below already read, so the shadows need no new branch and the
          # .onboarded gate is simply not consulted on this path. A typed
          # env var loses to a pressed key here, on purpose: the door is
          # the explicit choice. If boot_menu.py predates door 3 it never
          # returns 11, and this block is inert.
          if [ "$BOOT_CHOICE" -eq 11 ]; then
            PIPULATE_OPEN_JUPYTER=false
            PIPULATE_OPEN_FASTHTML=true
          fi
          # DOOR 1 ONLY: the banner, moved down from above the threshold on
          # 2026-08-25. Everything below this line runs only when the human
          # chose to start the app -- or when boot_menu.py is absent and the
          # FALL-THROUGH GUARANTEE puts us on the pre-menu path, where the
          # banner printed unconditionally anyway. The full version string
          # with its description rides here rather than in the readings line
          # above, because this is the one surface whose entire job is to name
          # the thing that is starting.
          figlet "$PROPER_APP_NAME"
          echo "Version: ${version}"
          # THE TAB KNOBS MOVE TO RUNTIME (2026-09-03, smallest possible step
          # toward swappable front tabs). autoOpenJupyter / autoOpenFastHTML
          # stay in Nix as the DEFAULTS; an env var typed at the prompt
          # shadows them for one entry, so "Pipulate tab only" is
          #   PIPULATE_OPEN_JUPYTER=false PIPULATE_OPEN_FASTHTML=true nix develop
          # with no flake edit and no new door. Nothing else changes: both
          # servers still start, and with no env vars set the readings are
          # byte-identical to before. A future door 3/4 sets these two
          # variables and nothing below needs to learn about it.
          OPEN_JUPYTER="''${PIPULATE_OPEN_JUPYTER:-${autoOpenJupyter}}"
          OPEN_FASTHTML="''${PIPULATE_OPEN_FASTHTML:-${autoOpenFastHTML}}"
          JUPYTER_BROWSER_FLAG=""
          if [ "$OPEN_JUPYTER" != "true" ]; then JUPYTER_BROWSER_FLAG="--no-browser"; fi
          # Automatically start JupyterLab in background and server in foreground
          # Start JupyterLab in a tmux session
          # NOTE: kill and launch stay PAIRED inside the branch. Splitting
          # them (kill outside, launch inside) would murder another
          # terminal's JupyterLab every time someone picked door 2.
[[[DIVIDER]]]
          # THE ONE DOOR: on an interactive tty boot_menu.py prints the short
          # command list and returns 10, so this child exits before either
          # server starts and the parent shell prompt becomes the introduction.
          # Automated lanes still return 0: no tty and PIPULATE_BOOT_MENU=0
          # preserve the pre-menu behavior exactly. Nothing here parses stdout.
          #
          # FALL-THROUGH GUARANTEE: if scripts/boot_menu.py is absent -- an
          # older checkout, a partial clone, a hand-deleted file -- the flake
          # behaves EXACTLY as it did before the menu existed. The flake must
          # never depend on a file that might not have landed.
          BOOT_CHOICE=0
          if [ -f scripts/boot_menu.py ]; then
            python scripts/boot_menu.py
            BOOT_CHOICE=$?
          fi
          if [ "$BOOT_CHOICE" -eq 10 ]; then
            # Nothing starts. runScript is EXECUTED (named on its own line in
            # each shellHook), never sourced, so exiting here ends this child
            # process and drops the parent into its interactive prompt. No tmux
            # session, TTS greeting, readiness poll or browser-open loop runs.
            exit 0
          fi
          # THE START PATH: reached only by an unattended/fail-open entry or by
          # a word such as `jn` that deliberately re-enters run-script with
          # PIPULATE_BOOT_MENU=0. The full version string rides here because
          # this is the surface whose job is to name the thing that is starting.
          figlet "$PROPER_APP_NAME"
          echo "Version: ${version}"
          # THE TAB KNOBS MOVE TO RUNTIME (2026-09-03, smallest possible step
          # toward swappable front tabs). autoOpenJupyter / autoOpenFastHTML
          # stay in Nix as the DEFAULTS; an env var typed at the prompt
          # shadows them for one entry, so "Pipulate tab only" is
          #   PIPULATE_OPEN_JUPYTER=false PIPULATE_OPEN_FASTHTML=true nix develop
          # with no flake edit and no new door. Nothing else changes: both
          # servers still start, and with no env vars set the readings are
          # byte-identical to before. Any explicit caller can set these two
          # variables without teaching the startup body another branch.
          OPEN_JUPYTER="''${PIPULATE_OPEN_JUPYTER:-${autoOpenJupyter}}"
          OPEN_FASTHTML="''${PIPULATE_OPEN_FASTHTML:-${autoOpenFastHTML}}"
          JUPYTER_BROWSER_FLAG=""
          if [ "$OPEN_JUPYTER" != "true" ]; then JUPYTER_BROWSER_FLAG="--no-browser"; fi
          # Automatically start JupyterLab in background and server in foreground
          # Start JupyterLab in a tmux session
          # NOTE: kill and launch stay PAIRED inside the branch. Splitting
          # them (kill outside, launch inside) would murder another
          # terminal's JupyterLab every time someone stayed at the shell prompt.
[[[REPLACE]]]
Target: flake.nix
[[[SEARCH]]]
          # THE SECOND DOOR, SPLIT IN TWO (2026-08-25). One word was doing two
          # unrelated jobs and nothing about the word said so: bare `mcp`
          # listed CONNECTORS that pull material IN, while `mcp <tool>`
          # dispatched a REGISTRY TOOL. Worse, `mcp` is the NAME OF A FILE --
          # connectors/mcp.py, the one thing here that genuinely IS
          # MCP -- and that file could not be reached by its own name because
          # the roster had taken it. Three words now, each of whose bare and
          # argument forms are about the same thing:
          #
          #   sources        the sources you can name (the roster)
          #   tools          the registry tools you can call
          #   tools <name>   call one
          #   mcp <server>   the real Streamable-HTTP client (aliased below)
          #
          # Discharges the CONNECTOR ALIAS TRANSITION earmark, whose text asked
          # only that the roster be renamed so `mcp` came free. `sources` beats
          # that earmark's own `connect` / `pipe` / `warp` for one reason: it
          # names the OUTPUT CLASS rather than an action the command does not
          # perform. The roster connects nothing; `warm` does.
          #
          # FALL-THROUGH GUARANTEE, and it now REFUSES instead of substituting:
          # if the roster file has not landed, say so in one line. The old
          # spelling fell through to `cli.py call` with no arguments, which
          # answered a DIFFERENT QUESTION with an argparse error. A shell must
          # never depend on a file that might not be there, and it must never
          # quietly answer something else when that file is missing.
          #
          # FUNCTIONS, NOT ALIASES (THE THREE-TIER AMENDMENT): `tools` needs a
          # branch, and both are typed by a human and never echoed as a `!`
          # probe, so neither needs to be a packaged derivation.
          sources() {
            if [ -f "$PIPULATE_ROOT/scripts/sources_menu.py" ]; then
              "$PIPULATE_ROOT/.venv/bin/python" "$PIPULATE_ROOT/scripts/sources_menu.py"
            else
              echo "sources: scripts/sources_menu.py has not landed in this checkout."
            fi
          }
[[[DIVIDER]]]
          # THE CONNECTOR ROSTER, SPLIT IN TWO (2026-08-25). One word was doing two
          # unrelated jobs and nothing about the word said so: bare `mcp`
          # listed CONNECTORS that pull material IN, while `mcp <tool>`
          # dispatched a REGISTRY TOOL. Worse, `mcp` is the NAME OF A FILE --
          # connectors/mcp.py, the one thing here that genuinely IS
          # MCP -- and that file could not be reached by its own name because
          # the roster had taken it. Three words now, each of whose bare and
          # argument forms are about the same thing:
          #
          #   conn           the connector sources you can name (the roster)
          #   tools          the registry tools you can call
          #   tools <name>   call one
          #   mcp <server>   the real Streamable-HTTP client (aliased below)
          #
          # The New-B surface now names the goal rather than the output class:
          # `conn` is the short word that gets connector sources into context.
          # The underlying roster file keeps its descriptive filename; there is
          # deliberately no compatibility alias for the old typed word.
          #
          # FALL-THROUGH GUARANTEE, and it now REFUSES instead of substituting:
          # if the roster file has not landed, say so in one line. The old
          # spelling fell through to `cli.py call` with no arguments, which
          # answered a DIFFERENT QUESTION with an argparse error. A shell must
          # never depend on a file that might not be there, and it must never
          # quietly answer something else when that file is missing.
          #
          # FUNCTIONS, NOT ALIASES (THE THREE-TIER AMENDMENT): `tools` needs a
          # branch, and both are typed by a human and never echoed as a `!`
          # probe, so neither needs to be a packaged derivation.
          conn() {
            if [ -f "$PIPULATE_ROOT/scripts/sources_menu.py" ]; then
              "$PIPULATE_ROOT/.venv/bin/python" "$PIPULATE_ROOT/scripts/sources_menu.py"
            else
              echo "conn: scripts/sources_menu.py has not landed in this checkout."
            fi
          }
[[[REPLACE]]]
Target: flake.nix
[[[SEARCH]]]
          # THE RECALL WORD. The door-two list prints ONCE, at the moment of
          # choice, and then scrolls away under whatever the human does next.
          # `menu` prints it again from the SAME tuple (DOOR_TWO_WORDS in
          # boot_menu.py), so the reminder can never drift from the original.
          #
          # A DISPLAY, NEVER A SECOND THRESHOLD, and the exit code is the whole
          # argument. boot_menu.py FAILS OPEN by design: no tty,
          # PIPULATE_BOOT_MENU=0, or any unexpected exception all return 0,
          # which means START THE APP. That polarity is correct at the
          # threshold, where blocking would strand an unattended nix develop.
          # At a PROMPT it inverts: a menu that branched on exit 0 would start
          # a server every time it ran without a tty -- including inside
          # prompt_foo's `!` executor, where stdout is a pipe, and `pu` would
          # then pkill the running server, start a new one in the foreground,
          # and hold the compile until the 180s process-group kill. So nothing
          # here reads the exit status, and --recall returns before the isatty
          # gate is ever reached. Door 1 already has a word here: `pu`.
          #
          # A FUNCTION, not writeShellScriptBin (THE THREE-TIER AMENDMENT):
          # only a human types this, and no child shell needs to resolve it.
          # That also makes it structurally invisible to the `!` executor, so
          # `type menu` can only ever be witnessed by a human at a real
          # prompt; the straddle reads the generated hook text instead.
          menu() {
            if [ -f "$PIPULATE_ROOT/scripts/boot_menu.py" ]; then
              "$PIPULATE_ROOT/.venv/bin/python" "$PIPULATE_ROOT/scripts/boot_menu.py" --recall
            else
              echo "menu: scripts/boot_menu.py has not landed in this checkout."
            fi
          }
          # THE THIRD DOOR: `pu` starts the server door 2 declined to start.
          # Kill-then-start, so it doubles as a restart and never collides on
          # :5001 -- the same pkill runScript has always run on shell entry.
          pu() {
            (
              cd "$PIPULATE_ROOT" || exit 1
              pkill -f "python server.py" || true
              python server.py
            )
          }
[[[DIVIDER]]]
          # THE RECALL WORDS. The short list prints once at shell entry and
          # scrolls away under whatever the human does next. `menu` recalls the
          # same SHORT_WORDS tuple; `all` prints the expanded ALL_WORDS tuple.
          #
          # A DISPLAY, NEVER A SECOND THRESHOLD, and the exit code is the whole
          # argument. boot_menu.py FAILS OPEN by design: no tty,
          # PIPULATE_BOOT_MENU=0, or any unexpected exception all return 0,
          # which means START THE APP. That polarity is correct at the
          # threshold, where blocking would strand an unattended nix develop.
          # At a PROMPT it inverts: a display wrapper must never branch on that
          # 0 or a redirected `menu` could start a server. So neither wrapper
          # reads the exit status; --recall returns before the tty gate.
          #
          # A FUNCTION, not writeShellScriptBin (THE THREE-TIER AMENDMENT):
          # only a human types this, and no child shell needs to resolve it.
          # That also makes it structurally invisible to the `!` executor, so
          # `type menu` can only ever be witnessed by a human at a real
          # prompt; the straddle reads the generated hook text instead.
          menu() {
            if [ -f "$PIPULATE_ROOT/scripts/boot_menu.py" ]; then
              "$PIPULATE_ROOT/.venv/bin/python" "$PIPULATE_ROOT/scripts/boot_menu.py" --recall
            else
              echo "menu: scripts/boot_menu.py has not landed in this checkout."
            fi
          }
          all() {
            if [ -f "$PIPULATE_ROOT/scripts/boot_menu.py" ]; then
              "$PIPULATE_ROOT/.venv/bin/python" "$PIPULATE_ROOT/scripts/boot_menu.py" --recall --all
            else
              echo "all: scripts/boot_menu.py has not landed in this checkout."
            fi
          }
          # `jn` is the old full startup path as a word, not a second copy of
          # that path. Re-enter run-script with the threshold explicitly
          # declined so JupyterLab and Pipulate start exactly as they do on an
          # unattended entry, with JupyterLab in front under the current defaults.
          jn() {
            (cd "$PIPULATE_ROOT" && PIPULATE_BOOT_MENU=0 ${runScript}/bin/run-script)
          }
          # `pu` is the server-only word. Kill-then-start, so it doubles as a
          # restart and never collides on :5001.
          pu() {
            (
              cd "$PIPULATE_ROOT" || exit 1
              pkill -f "python server.py" || true
              python server.py
            )
          }
[[[REPLACE]]]
Target: flake.nix
[[[SEARCH]]]
          # named. Freed 2026-08-25 when the roster became `sources`, and
[[[DIVIDER]]]
          # named. Freed 2026-08-25 when the roster moved off `mcp`, and
[[[REPLACE]]]
Target: flake.nix
[[[SEARCH]]]
          cpr() { (cd "$PIPULATE_ROOT" && PIPULATE_ADHOC_FILE="$(_walkrouter)" python prompt_foo.py "$@" --chop ADHOC_CHOP --no-tree); }
[[[DIVIDER]]]
          cpr() {
            local router="$(_walkrouter)"
            (
              cd "$PIPULATE_ROOT" || exit 1
              PIPULATE_ADHOC_FILE="$router" python prompt_foo.py "$@" --chop WALK_CHOP --no-tree --quiet
            ) || return $?
            echo "Read: $router"
            if [ -n "''${SSH_CLIENT:-}" ]; then
              echo "Delivery lane: /tmp/clipboard_bridge.txt"
            else
              echo "Delivery lane: system clipboard"
            fi
          }
[[[REPLACE]]]
Target: flake.nix
[[[SEARCH]]]
              "You are Yen Sid-ton, the onboarding wizard for Pipulate. A newcomer wants to install Pipulate for the first time. Your FIRST reply must be short and do four things in order: (1) confirm in one line that you hold the full install map (installer, flake, both Pipulate.com pages); (2) show the one-line install command immediately, since it is identical on every OS; (3) ask exactly one question, which OS they are on, noting it changes only the caveats, never the command; (4) add one line noting the command assumes Nix is already installed, and that a nix command not found response means install Nix first and reopen the terminal. From then on: one step per turn, one question maximum per turn, and every step ends with a visible success checkpoint describing what they should literally see (the one-line environment readings, the three-door menu where they press 1, the figlet banner once the app starts, the JupyterLab URL) plus the single most likely failure symptom at that step and its fix. Deliver the macOS --impure exception and the reopen-your-terminal-after-installing-Nix requirement at the moment each can bite, never as an upfront lecture. Offer the magic cookie internals (ZIP + ROT13 key, then git transformation and auto-updates inside nix develop) as an optional aside when relevant or when asked, not as mandatory explanation. When both the server and JupyterLab are confirmed running, declare the install banked and teach the re-entry incantation: cd into the install folder, then nix develop. High signal, low noise. Ask them what they see; never assume." \
[[[DIVIDER]]]
              "You are Yen Sid-ton, the onboarding wizard for Pipulate. A newcomer wants to install Pipulate for the first time. Your FIRST reply must be short and do four things in order: (1) confirm in one line that you hold the full install map (installer, flake, both Pipulate.com pages); (2) show the one-line install command immediately, since it is identical on every OS; (3) ask exactly one question, which OS they are on, noting it changes only the caveats, never the command; (4) add one line noting the command assumes Nix is already installed, and that a nix command not found response means install Nix first and reopen the terminal. From then on: one step per turn, one question maximum per turn, and every step ends with a visible success checkpoint describing what they should literally see (the one-line environment readings, the short command list at the shell prompt, the figlet banner after they type jn, the JupyterLab URL) plus the single most likely failure symptom at that step and its fix. Deliver the macOS --impure exception and the reopen-your-terminal-after-installing-Nix requirement at the moment each can bite, never as an upfront lecture. Offer the magic cookie internals (ZIP + ROT13 key, then git transformation and auto-updates inside nix develop) as an optional aside when relevant or when asked, not as mandatory explanation. When both the server and JupyterLab are confirmed running, declare the install banked and teach the re-entry incantation: cd into the install folder, then nix develop. High signal, low noise. Ask them what they see; never assume." \
[[[REPLACE]]]
Target: foo_files.py
[[[SEARCH]]]
# scripts/boot_menu.py        # <-- The three-door workshop menu; walk, epr and cpr lead door 2's seven words, the count derived from the tuple
# scripts/sources_menu.py     # <-- The roster door 2's words print; not a connector executor
[[[DIVIDER]]]
# scripts/boot_menu.py        # <-- The one-door command list; short on shell entry, expanded by `all`, both rendered from tuples in the file
# scripts/sources_menu.py     # <-- The roster `conn` prints; filename stays descriptive, typed word is shorter
[[[REPLACE]]]
Target: foo_files.py
[[[SEARCH]]]
# wrong sentence in the sources panel: fix the file, then this comment.
[[[DIVIDER]]]
# wrong sentence in the conn roster: fix the file, then this comment.
[[[REPLACE]]]
Target: foo_files.py
[[[SEARCH]]]
# scripts/sources_menu.py          # <-- what door 2 opens onto.
[[[DIVIDER]]]
# scripts/sources_menu.py          # <-- what `conn` prints.
[[[REPLACE]]]
Target: foo_files.py
[[[SEARCH]]]
PINNED_CHOP = r"""
[[[DIVIDER]]]
WALK_CHOP = r"""
# THE WALK ROUTER (only the disposable overlay)
# COMMAND: PIPULATE_ADHOC_FILE=<router> python prompt_foo.py --chop WALK_CHOP --no-tree --quiet
# cpr points PIPULATE_ADHOC_FILE at the walk-written router. This chop carries
# no fixed tail: every payload line between the slot markers comes from that
# explicit overlay file.

# --- ADHOC SLOT START ---
# (Structurally empty in the repo. cpr supplies the walk router at compile time.)
# --- ADHOC SLOT END ---
"""

PINNED_CHOP = r"""
[[[REPLACE]]]
Target: foo_files.py
[[[SEARCH]]]
# - TODO (2026-09-16, door two): plan is absent from DOOR_TWO_WORDS because _COUNT_WORDS spells up to seven and an eighth word prints a digit; extend the tuple through ten, then add plan after cpr, and the recall probe reads Eight words.
[[[DIVIDER]]]
# - TODO (2026-09-16, command list): plan remains absent from ALL_WORDS. If it earns a newcomer-facing word, add it to the expanded list only; the short list stays the Prompt Fu loop.
[[[REPLACE]]]
Target: foo_files.py
[[[SEARCH]]]
# - TODO (2026-09-15, operator ruling): replace jeopardy.wav under the summoning with free-licensed or public-domain audio, a ticking clock while the browser waits and an egg-timer ding at the end of the wait.
# - TODO (2026-09-15, next door): the walk door in scripts/boot_menu.py, with its own exit code and runScript branch so exit 0 keeps meaning start the app and Enter keeps starting it, plus one label sweep across boot_menu.py, the flake's voice greeting and the brief prompt; ride it after the DECANT ride.
[[[DIVIDER]]]
# - TODO (2026-09-15, operator ruling; mechanism landed with THE ONE DOOR): supply copyright-free tick.wav and ding.wav under ~/.local/share/pipulate/. The scraper now uses them when present, afplay on Darwin/aplay elsewhere, and stays silent when absent; no wav ships in git.
[[[REPLACE]]]

Car 2 โ€” print before speak + one intro contract + blank-line narration

Target: scripts/mother_cat.py
[[[SEARCH]]]
def _narrate(text, disclosed, indent="  "):
    """Speak scripted guidance if Piper is available; never gate the ride.

    indent is the printed line's left margin. Two spaces under a stop header
    read as nesting; the walk's opening sentence sits at the top level, wraps,
    and an indented first line over flush continuations reads as a mistake
    (operator, 2026-09-17), so that call passes none.
    """
    try:
        from imports.voice_synthesis import chip_voice_system
    except Exception as exc:
        print(f"{indent}(voice import unavailable: {exc}) {text}")
        return disclosed

    if chip_voice_system is None:
        print(f"{indent}(voice unavailable) {text}")
        return disclosed

    # THE NARRATION VANISHED WITH ITS OWN ERROR (convicted 2026-08-02, ride
    # five): the stop guidance reached the human ONLY inside a voice-failure
    # message. When the per-user lock fix made speak_text stop failing, the
    # printed text disappeared with the error that had been carrying it, and
    # the ride delivered NEITHER audio NOR words -- the NARRATE beat of the
    # kata became a silent no-op that reported success. Print first, then
    # speak, so the visible channel never depends on the audible one failing.
    print(f"{indent}{text}")
    try:
        if not disclosed:
            result = chip_voice_system.speak_text(
                "This is Piper, a small program reading written instructions "
                "aloud. It does not listen or answer questions."
            )
            if isinstance(result, dict) and result.get("declined"):
                # A human said no, or nobody has been asked: not a failure,
                # so no failure line. The printed text above is the channel.
                return True
            if isinstance(result, dict) and not result.get("success"):
                print(
                    "  (voice disclosure failed: "
                    f"{result.get('error', 'unknown error')})"
                )
            disclosed = True

        result = chip_voice_system.speak_text(text)
        if isinstance(result, dict) and result.get("declined"):
            return disclosed
        if isinstance(result, dict) and not result.get("success"):
            print(
                "  (voice guidance failed: "
                f"{result.get('error', 'unknown error')})"
            )
    except Exception as exc:
        print(f"{indent}(voice error, continuing: {exc}) {text}")

    return disclosed

[[[DIVIDER]]]
def _narrate(text, disclosed):
    """Speak scripted guidance if Piper is available; never gate the ride.

    The visible channel always lands first, before importing or invoking Piper.
    A blank line supplies separation without an indent that disappears when an
    eighty-column terminal wraps the guidance.
    """
    print()
    print(text)

    try:
        from imports.voice_synthesis import chip_voice_system
    except Exception as exc:
        print(f"(voice import unavailable: {exc})")
        return disclosed

    if chip_voice_system is None:
        print("(voice unavailable)")
        return disclosed

    # THE NARRATION VANISHED WITH ITS OWN ERROR (convicted 2026-08-02, ride
    # five): the stop guidance reached the human ONLY inside a voice-failure
    # message. When the per-user lock fix made speak_text stop failing, the
    # printed text disappeared with the error that had been carrying it, and
    # the ride delivered NEITHER audio NOR words -- the NARRATE beat of the
    # kata became a silent no-op that reported success. Print first, then
    # speak, so the visible channel never depends on the audible one failing.
    try:
        if not disclosed:
            result = chip_voice_system.speak_text(
                "This is Piper, a small program reading written instructions "
                "aloud. It does not listen or answer questions."
            )
            if isinstance(result, dict) and result.get("declined"):
                # A human said no, or nobody has been asked: not a failure,
                # so no failure line. The printed text above is the channel.
                return True
            if isinstance(result, dict) and not result.get("success"):
                print(
                    "  (voice disclosure failed: "
                    f"{result.get('error', 'unknown error')})"
                )
            disclosed = True

        result = chip_voice_system.speak_text(text)
        if isinstance(result, dict) and result.get("declined"):
            return disclosed
        if isinstance(result, dict) and not result.get("success"):
            print(
                "  (voice guidance failed: "
                f"{result.get('error', 'unknown error')})"
            )
    except Exception as exc:
        print(f"(voice error, continuing: {exc})")

    return disclosed

[[[REPLACE]]]
Target: scripts/mother_cat.py
[[[SEARCH]]]
def _announce_consent(trail_path, intro=False):
    """Describe capture and handoff, never grant authorization here.

    Custom walks use the same trail projection as walk_cartridge.
    The bundled introduction uses the shared plain-language contract;
    --intro is validated separately before any narration or capture.
    """
    try:
        surface = walk_cartridge._derive_consent_surface(trail_path.read_bytes())
    except (OSError, ValueError) as exc:
        # FAIL SOFT AND LOUD. walk.load_trail has already validated this file
        # far more strictly than this projection does, so a refusal HERE means
        # two authorities disagree about one file. That is information worth
        # printing, not a reason to abort a ride the planner already blessed.
        print(f"  (consent surface unavailable: {exc})")
        return
    if intro:
        print("\n" + INTRO_NOTICE)
        print(f"Summary file: {DECANT_PREVIEW_PATH.relative_to(REPO_ROOT)} (private; replaced on save).")
        print("Checks can miss private details. A blocked check leaves the older file alone.\n")
        return
[[[DIVIDER]]]
def _announce_consent(trail_path, intro=False):
    """Describe capture and handoff, never grant authorization here.

    Custom walks use the same trail projection as walk_cartridge.
    The launcher owns the bundled plain-language INTRO_NOTICE; --intro avoids
    printing it a second time and adds only the local summary/check lines.
    """
    try:
        surface = walk_cartridge._derive_consent_surface(trail_path.read_bytes())
    except (OSError, ValueError) as exc:
        # FAIL SOFT AND LOUD. walk.load_trail has already validated this file
        # far more strictly than this projection does, so a refusal HERE means
        # two authorities disagree about one file. That is information worth
        # printing, not a reason to abort a ride the planner already blessed.
        print(f"  (consent surface unavailable: {exc})")
        return
    if intro:
        print()
        print(f"Summary file: {DECANT_PREVIEW_PATH.relative_to(REPO_ROOT)} (private; replaced on save).")
        print("Checks can miss private details. A blocked check leaves the older file alone.\n")
        return
[[[REPLACE]]]
Target: scripts/mother_cat.py
[[[SEARCH]]]
    _ask_voice()
[[[DIVIDER]]]
    print("Make sure your audio is turned on.\n")
    _ask_voice()
[[[REPLACE]]]
Target: scripts/mother_cat.py
[[[SEARCH]]]
    disclosed = _narrate(rehearsal + trail["description"], False, indent="")
[[[DIVIDER]]]
    disclosed = _narrate(rehearsal + trail["description"], False)
[[[REPLACE]]]

Car 3 โ€” CAPTURE handoff narration hook

Target: scripts/mother_cat.py
[[[SEARCH]]]
        params = walk._browser_params(url, trail["defaults"])
        result = await guided_browser_capture(
            params,
            stdin=sys.stdin,
            stdout=sys.stdout,
        )
[[[DIVIDER]]]
        params = walk._browser_params(url, trail["defaults"])
        def checkpoint_narration():
            nonlocal disclosed
            disclosed = _narrate(
                "The page is open. When it looks finished, come back to the terminal and type CAPTURE.",
                disclosed,
            )

        result = await guided_browser_capture(
            params,
            stdin=sys.stdin,
            stdout=sys.stdout,
            before_prompt=checkpoint_narration,
        )
[[[REPLACE]]]
Target: tools/scraper_tools.py
[[[SEARCH]]]
def _capture_checkpoint(stdin=None, stdout=None) -> dict:
[[[DIVIDER]]]
def _capture_checkpoint(stdin=None, stdout=None, before_prompt=None) -> dict:
[[[REPLACE]]]
Target: tools/scraper_tools.py
[[[SEARCH]]]
        try:
            output_stream.write(
                "\nWhen the page you want is ready, type CAPTURE and press Enter.\n"
                "Any other response aborts without capturing artifacts.\n"
                "CAPTURE> "
            )
            output_stream.flush()
[[[DIVIDER]]]
        try:
            if before_prompt is not None:
                try:
                    before_prompt()
                except Exception as exc:
                    output_stream.write(f"\n(checkpoint narration unavailable: {exc})\n")
            else:
                output_stream.write(
                    "\nWhen the page you want is ready, type CAPTURE and press Enter.\n"
                )
            output_stream.write(
                "Any other response aborts without capturing artifacts.\n"
                "CAPTURE> "
            )
            output_stream.flush()
[[[REPLACE]]]
Target: tools/scraper_tools.py
[[[SEARCH]]]
async def guided_browser_capture(params: dict, stdin=None, stdout=None) -> dict:
[[[DIVIDER]]]
async def guided_browser_capture(params: dict, stdin=None, stdout=None, before_prompt=None) -> dict:
[[[REPLACE]]]
Target: tools/scraper_tools.py
[[[SEARCH]]]
        checkpoint=lambda: _capture_checkpoint(
            stdin=input_stream,
            stdout=stdout,
        ),
    )

[[[DIVIDER]]]
        checkpoint=lambda: _capture_checkpoint(
            stdin=input_stream,
            stdout=stdout,
            before_prompt=before_prompt,
        ),
    )

[[[REPLACE]]]

Car 4 โ€” guided capture skips the 20-second challenge wait

Target: tools/scraper_tools.py
[[[SEARCH]]]
        logger.info(f"Navigating to: {url}")
        driver.get(url)

        try:
            if verbose: logger.info("Waiting for security challenge to trigger a reload (Stage 1)...")
            initial_body = driver.find_element(By.TAG_NAME, 'body')
            WebDriverWait(driver, 20).until(EC.staleness_of(initial_body))
            if verbose: logger.success("โœ… Page reload detected!")
            
            if verbose: logger.info("Waiting for main content to appear after reload (Stage 2)...")
            WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "body")))
            if verbose: logger.success("โœ… Main content located!")
        except Exception as e:
            if verbose: logger.info(f"Did not detect a page reload for security challenge. Proceeding anyway.")

        # The wait is over one way or the other: cut the think-music sharply.
        _stop_scrape_music(music_proc)
        music_proc = None

[[[DIVIDER]]]
        logger.info(f"Navigating to: {url}")
        driver.get(url)

        # A human-guided ride already has the CAPTURE fence as its settle
        # detector. The 20-second staleness wait exists for unattended sigil
        # scrapes, where a security interstitial may reload underneath us.
        if not interactive:
            try:
                if verbose: logger.info("Waiting for security challenge to trigger a reload (Stage 1)...")
                initial_body = driver.find_element(By.TAG_NAME, 'body')
                WebDriverWait(driver, 20).until(EC.staleness_of(initial_body))
                if verbose: logger.success("โœ… Page reload detected!")

                if verbose: logger.info("Waiting for main content to appear after reload (Stage 2)...")
                WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "body")))
                if verbose: logger.success("โœ… Main content located!")
            except Exception:
                if verbose: logger.info("Did not detect a page reload for security challenge. Proceeding anyway.")

        # The page-load phase is over: stop ticking and, if the operator
        # supplied one, play the ready sound exactly once.
        _stop_scrape_music(music_proc, ding=True)
        music_proc = None

[[[REPLACE]]]
Target: tools/scraper_tools.py
[[[SEARCH]]]
            # staleness_of() wait above exists because a security challenge
            # serves an interstitial Document and then reloads, making the LAST
[[[DIVIDER]]]
            # non-interactive staleness_of() wait above exists because a security
            # challenge serves an interstitial Document and then reloads, making the LAST
[[[REPLACE]]]

Car 5 โ€” local tick/ding, afplay on Darwin, silence when absent

Target: tools/scraper_tools.py
[[[SEARCH]]]
import os
import sys
[[[DIVIDER]]]
import os
import shlex
import sys
[[[REPLACE]]]
Target: tools/scraper_tools.py
[[[SEARCH]]]
# --- Optional audio during browser startup ---
# Forked from stream.py's start_updating_music/stop_updating_music pattern:
# a marker-tagged shell loop in its OWN process group (os.setsid), killed as
# a group, with an idempotent pkill backstop keyed to the marker so it can
# never touch any other aplay pipeline. The wav lives in repo negative space
# (gitignored) or ~/.local/share/pipulate/; absent file = silent no-op.
SCRAPE_MUSIC_MARKER = "pipulate-scrape-music"

def _find_music_file():
    candidates = [
        Path(__file__).resolve().parent.parent / "jeopardy.wav",
        Path.home() / ".local/share/pipulate/jeopardy.wav",
        Path.home() / ".local/share/honeybot/jeopardy.wav",
    ]
    for c in candidates:
        if c.exists():
            return c
    return None

def _start_scrape_music(verbose=True):
    import subprocess
    music = _find_music_file()
    if not music:
        return None

    try:
        return subprocess.Popen(
            ["sh", "-c", f'while :; do aplay -q -D default "{music}"; done # {SCRAPE_MUSIC_MARKER}'],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            preexec_fn=os.setsid,
        )
    except Exception:
        return None

def _stop_scrape_music(proc):
    import signal
    import subprocess
    if proc is not None:
        try:
            os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
        except Exception:
            pass
    try:
        subprocess.run(["pkill", "-f", SCRAPE_MUSIC_MARKER], check=False)
    except Exception:
        pass

[[[DIVIDER]]]
# --- Optional audio during browser startup ---
# Operator-owned courtesy sounds live outside the repo. tick.wav loops while
# the browser is opening; ding.wav plays once when the page-load phase ends.
# No file means no sound. Linux uses aplay; Darwin uses afplay.
SCRAPE_MUSIC_MARKER = "pipulate-scrape-music"
SOUND_DIR = Path.home() / ".local/share/pipulate"

def _sound_file(name):
    path = SOUND_DIR / name
    return path if path.is_file() else None

def _player_args(sound):
    if sound is None:
        return None
    if sys.platform == "darwin":
        player = shutil.which("afplay")
        return [player, str(sound)] if player else None
    player = shutil.which("aplay")
    return [player, "-q", "-D", "default", str(sound)] if player else None

def _start_scrape_music(verbose=True):
    import subprocess

    args = _player_args(_sound_file("tick.wav"))
    if not args:
        return None
    command = " ".join(shlex.quote(part) for part in args)
    try:
        return subprocess.Popen(
            ["sh", "-c", f"while :; do {command}; done # {SCRAPE_MUSIC_MARKER}"],
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            preexec_fn=os.setsid,
        )
    except Exception:
        return None

def _stop_scrape_music(proc, ding=False):
    import signal
    import subprocess

    if proc is not None:
        try:
            os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
        except Exception:
            pass
    try:
        subprocess.run(
            ["pkill", "-f", SCRAPE_MUSIC_MARKER],
            check=False,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
        )
    except Exception:
        pass
    if ding:
        args = _player_args(_sound_file("ding.wav"))
        if args:
            try:
                subprocess.run(
                    args,
                    check=False,
                    stdout=subprocess.DEVNULL,
                    stderr=subprocess.DEVNULL,
                )
            except Exception:
                pass

[[[REPLACE]]]

Car 6 โ€” bank the two publishing earmarks; no publishing code touched

Target: foo_files.py
[[[SEARCH]]]
# - TODO (2026-09-04, demoted from the THREE-TIER WORKSPACE earmark; the tiers and the ladder are vocabulary now, rg -in 'three-tier workspace' GLOSSARY.md): extend server.py's app scanner to APP_SEARCH_PATHS across apps/, Notebooks/Corporate/apps/ and Notebooks/Personal/apps/; register the workspace tree as sealed figurate art before any surface copies it; one figlet banner per Prompt Fu alias, then silence; recolor startup_environment_warnings() off bright_red on onboarding surfaces. Gate: a tool dropped in Notebooks/Personal/apps/ appears in the APP menu with no core commit.
[[[DIVIDER]]]
# - EARMARK (2026-09-18, publishing lane): Jekyll article navigation is asymmetric -- Previous renders, but Next is missing even when viewing an older article that does have a newer neighbor. Reproduce on localhost and live Web, then trace the template/data source that computes the forward neighbor; not part of THE ONE DOOR ride.
# - EARMARK (2026-09-18, publishing lane): the Google Doc share link is stamped into the article only after the git-push release, so the live site trails localhost by one publish. Reorder or add a second publish boundary only after tracing googledocizer/release ownership; symptom banked here, cause not asserted.
# - TODO (2026-09-04, demoted from the THREE-TIER WORKSPACE earmark; the tiers and the ladder are vocabulary now, rg -in 'three-tier workspace' GLOSSARY.md): extend server.py's app scanner to APP_SEARCH_PATHS across apps/, Notebooks/Corporate/apps/ and Notebooks/Personal/apps/; register the workspace tree as sealed figurate art before any surface copies it; one figlet banner per Prompt Fu alias, then silence; recolor startup_environment_warnings() off bright_red on onboarding surfaces. Gate: a tool dropped in Notebooks/Personal/apps/ appears in the APP menu with no core commit.
[[[REPLACE]]]

IGNITION: after the last car, exit the current shell and run nix develop. The first interactive screen should be the six-word short list; type all to witness the nine-word expanded list.

  1. PROMPT

Caboose for prompt.md:

THE ONE DOOR AFTER.

Read the AFTER probes first and the pasted first screen from both Linux and Mac.

Verify the interactive threshold is now the six-word short list with no numeric panel; env/no-tty still return 0; `all` is the nine-word expanded list carrying brief/jn/pu and no wake; conn has no sources alias; WALK_CHOP is slot-only and cpr uses it with --quiet, naming the router and delivery lane without claiming clipboard success; old panel labels are gone.

Verify the rider separately: every narration prints before Piper import/speech, the audio-on line is first, INTRO_NOTICE prints once through the launcher, the checkpoint handoff says exactly "The page is open. When it looks finished, come back to the terminal and type CAPTURE.", the guided lane skips the 20-second staleness wait while the sigil lane retains it, and local tick.wav/ding.wav use afplay on Darwin/aplay elsewhere with silence when absent.

The walk's own Practice/Start/Exit menu is still unruled and untouched. `wake` is still unruled and has no mechanism.

Then take the next ruled ride only: prompt_foo.py's checklist frame for cpr, so a page question no longer drags five cars. Use the current WALK_CHOP and prompt_foo.py raw source in context, preserve ahc/default behavior, and give it one falsifying straddle.

Do not reopen publishing code here: the missing Jekyll Next arrow and one-release-late Google Doc link are now banked earmarks in foo_files.py.
  1. EXTERNAL DELIVERABLES

The only manual external artifact is the courtesy audio: acquire copyright-free or public-domain tick.wav and ding.wav and place them under ~/.local/share/pipulate/ on each machine where you want sound. Their absence is intentionally a silent no-op. For the next turn, also paste the first post-nix develop interactive screen from Linux and Mac so the actual terminal boundaryโ€”not just the sourceโ€”becomes the receipt.

MikeLev.in:

Same commands, run twice, one change between them. Where the readings differ is what the change did; the diff in the middle is the receipt.

1: Probe: (BEFORE: hand-run, nothing changed yet)

(nix) pipulate $ g

Blast Radius Check to establish bisection 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 $ .venv/bin/python -m py_compile scripts/boot_menu.py scripts/mother_cat.py tools/scraper_tools.py foo_files.py
.venv/bin/python scripts/boot_menu.py --recall
.venv/bin/python scripts/boot_menu.py --recall --all
PIPULATE_BOOT_MENU=0 .venv/bin/python scripts/boot_menu.py; printf 'env_rc=%s\n' "$?"
printf '' | .venv/bin/python scripts/boot_menu.py; printf 'pipe_rc=%s\n' "$?"
rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-120
rg -n 'sources\(\)|echo "sources:|DOOR_TWO_WORDS|three-door workshop menu|door 2 opens onto|sources panel' flake.nix foo_files.py scripts/boot_menu.py
rg -n 'WALK_CHOP|--chop WALK_CHOP|Read: \$router|Delivery lane:' flake.nix foo_files.py prompt_foo.py | cut -c1-140
rg -n 'before_prompt|staleness_of|tick\.wav|ding\.wav|afplay|aplay' tools/scraper_tools.py scripts/mother_cat.py | cut -c1-140
rg -n 'def _narrate|Make sure your audio is turned on|The page is open|INTRO_NOTICE' scripts/mother_cat.py | cut -c1-140
nix-instantiate --parse flake.nix >/dev/null

Seven words to start from:
  walk      take the guided tour -- public pages, nothing to log into
  epr       edit the prompt router -- the list the walk wrote of what an AI should see, and your question
  cpr       compile the prompt router into your clipboard -- paste it into an AI
  sources   see what this shell can reach outside this machine
  brief     compile this workshop into your clipboard for an AI -- the context compiler's first job
  pu        change your mind and start the app server after all
  menu      print this list again once it scrolls away

Seven words to start from:
  walk      take the guided tour -- public pages, nothing to log into
  epr       edit the prompt router -- the list the walk wrote of what an AI should see, and your question
  cpr       compile the prompt router into your clipboard -- paste it into an AI
  sources   see what this shell can reach outside this machine
  brief     compile this workshop into your clipboard for an AI -- the context compiler's first job
  pu        change your mind and start the app server after all
  menu      print this list again once it scrolls away
env_rc=0
pipe_rc=0
scripts/boot_menu.py:6:  [1] JupyterLab tab     both servers start; JupyterLab opens in the browser (today's default)
scripts/boot_menu.py:8:  [3] Pipulate tab       both servers start; the app opens in the browser instead of JupyterLab
scripts/boot_menu.py:164:        "[1]  JupyterLab tab     both servers start; JupyterLab opens in the browser",
scripts/boot_menu.py:166:        "[3]  Pipulate tab       both servers start; the app opens in the browser",
scripts/boot_menu.py:185:                title="nix develop -- a reproducible *nix shell :: pick a door",
scripts/boot_menu.py:193:        print("--- nix develop -- a reproducible *nix shell :: pick a door ---")
flake.nix:1737:              "You are Yen Sid-ton, the onboarding wizard for Pipulate. A newcomer wants to install Pipul
foo_files.py:1486:# scripts/boot_menu.py        # <-- The three-door workshop menu; walk, epr and cpr lead door 2's seve
scripts/boot_menu.py
89:DOOR_TWO_WORDS = (
115:    DOOR_TWO_WORDS is data rather than two hand-written strings.
117:    print(_count_word(len(DOOR_TWO_WORDS)).capitalize() + " words to start from:")
118:    width = max(len(word) for word, _ in DOOR_TWO_WORDS)
119:    for word, description in DOOR_TWO_WORDS:
165:        f"[2]  Text Commands      nothing starts -- {_count_word(len(DOOR_TWO_WORDS))} words wait at the prompt",

flake.nix
1356:          sources() {
1360:              echo "sources: scripts/sources_menu.py has not landed in this checkout."
1372:          # `menu` prints it again from the SAME tuple (DOOR_TWO_WORDS in

foo_files.py
1486:# scripts/boot_menu.py        # <-- The three-door workshop menu; walk, epr and cpr lead door 2's seven words, the count derived from the tuple
1883:# wrong sentence in the sources panel: fix the file, then this comment.
1891:# scripts/sources_menu.py          # <-- what door 2 opens onto.
2278:# - TODO (2026-09-16, door two): plan is absent from DOOR_TWO_WORDS because _COUNT_WORDS spells up to seven and an eighth word prints a digit; extend the tuple through ten, then add plan after cpr, and the recall probe reads Eight words.
tools/scraper_tools.py:77:# never touch any other aplay pipeline. The wav lives in repo negative space
tools/scraper_tools.py:102:            ["sh", "-c", f'while :; do aplay -q -D default "{music}"; done # {SCRAPE_MUSIC_MARKER}'],
tools/scraper_tools.py:614:            WebDriverWait(driver, 20).until(EC.staleness_of(initial_body))
tools/scraper_tools.py:756:            # staleness_of() wait above exists because a security challenge
50:def _narrate(text, disclosed, indent="  "):
147:# the resolved bundled three-page route. The launcher prints INTRO_NOTICE
161:INTRO_NOTICE = (
726:        print("\n" + INTRO_NOTICE)
894:    # The launcher discloses INTRO_NOTICE before its real-walk choice and
1300:                print(INTRO_NOTICE)
nix-instantiate: /nix/store/mg1284kfh1m2xms1ghsw4nv8vhqisj22-openssl-3.0.14/lib/libssl.so.3: version `OPENSSL_3.2.0' not found (required by /nix/store/fn0k2qlkgicm4z1s53z6132kkzi5izlr-curl-8.21.0/lib/libcurl.so.4)
nix-instantiate: /nix/store/mg1284kfh1m2xms1ghsw4nv8vhqisj22-openssl-3.0.14/lib/libssl.so.3: version `OPENSSL_3.5.0' not found (required by /nix/store/ig1dybzs8qr3vwfsqlj1dcszbag1crpk-ngtcp2-1.23.0/lib/libngtcp2_crypto_ossl.so.0)
nix-instantiate: /nix/store/r8qsxm85rlxzdac7988psm7gimg4dl3q-glibc-2.39-52/lib/libc.so.6: version `GLIBC_ABI_DT_X86_64_PLT' not found (required by /nix/store/ias8xacs1h3jy7xgwi2awvim61k2ji6c-glibc-2.42-67/lib/libresolv.so.2)
(nix) pipulate $ 

2: Context: (AFTER: the same probes re-run by the compiler as ! lines)

# AD HOC CHOP! The Not-Managed-by-Git Safe-for-Client-Data place. Insert Simpson Couch Gag in white space  below (explain anything to the audience you feel needs it explained)G
# adhoc.txt    _   _   _               ____ _   _  ___  ____  _   
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  A nap has been had. I can think about this next step.
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  Anthropic dropped the ACME Anvil on Wile E. Coyote's head. Thanks a lot Lutnick. Well I get to demonstrate to everyone how ChatGPT 6 and not even Pro but just "Extra High" can do this stuff every bit as well as Fable 5.1. Apparently the Coyote could only go to ACME but I can go to Ajax Corporation which is hilariously apropos for the Web.
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  

# To Human: WELCOME TO VIM! It's really NeoVim but I say vim anyway.
# To AI: This is an alternate 40K view to the big book-ore rolling pin spine.

# 1. **Probe**: Baseline Reading
# 2. **Context**: Post-experiment *planned* reading instructions
# 3. **Patch**: The experiment and how to make it happen
# 4. **Prompt**: Post-experiment instructions and how to read results
# 5. **Deliverable**: How the world is forever different moving forward

# The first thing you need to know here is that everything that comes after the
# hash symbol (#) is commented out โ€” and that's EVERYTHING in this file's default
# state. Begin editing-in lines for inclusion as part of the context or adding
# chunks of new context at the bottom. `Ctrl`+`v`, `j` (repeatedly), `l` (to move
# right), `d` (to delete). Reverse that with `Ctrl`+`v`, `j` (repeatedly),
# `Shift`+`i`, `# `, `Esc` to put the hashes back. You can just arrow-key around
# here with `h`, `j`, `k`, `l`. Save-and-quit is a bit tricky because another
# file is also loaded: `Esc`, `:`, `q`, `w`, `!`

# If this is stressing you out and you're a quitter and want to quit, just type:
# `Esc`, `:`, `q`, `!`, `Enter`. That will exit without saving any changes. If
# you want to get over this hump, type: `Esc`, `:`, `T`, `u`, `t`, `o`, `r`, `Enter`.

# This file is just to make it easy having options of what to edit into context.
# You can use whatever text-file you want to stack file-names and commands to
# build an output text-file with the identically stacked output of each file or
# command. In this way we vertically append or "stack" a bunch of text; simple as
# that. If you understand this concept, you're on your way to future-proofing
# yourself in the Age of AI. Congratulations! Here is how to include web pages:

#    !URL  --------------------------------------------------------------------
#      when    Public page; what a stranger or crawler sees; the BEFORE of a
#              login-wall diagnosis
#      switch  It shows a login page -> `warm URL` once, then `?URL`
#    
#    ?URL  --------------------------------------------------------------------
#      when    Anything behind a login, on the site's persistent profile;
#              `check URL` first
#      switch  The lenses show a shell (nav, an `[Iframe]` leaf, no content) ->
#              read the wire truth for the XHR the frame makes, then call that
#              API with a connector
#    
#    @URL  --------------------------------------------------------------------
#      when    Every re-read of a page already scraped; no browser, no network
#      switch  The cached page is stale or was a login wall -> fresh `!` or `?`
#    
#    $URL  --------------------------------------------------------------------
#      when    Exact markup: meta tags, a JSON blob in a `<script>`
#      note    Token-heavy; needs a prior scrape
#    
#    %URL  --------------------------------------------------------------------
#      when    The network log distilled; SPA endpoint discovery
#      switch  It re-serves the wire truth you already have -> the API
#    
#    ! cmd  -------------------------------------------------------------------
#      when    Any bounded, non-interactive command as a live receipt
#      note    Cap it with `-n`; no aliases, no prompts
#    
#    Connector  ---------------------------------------------------------------
#      when    The number you want is one GET away
#      switch  LIST until the thing isn't in the list -> FETCH by id -> DRILL
#              the path the app's own frame called -> `--grep` to narrow a list
#              or find a leaf

# Every step is one argument longer than the last; the moment a lens shows less than the wire does is the moment to stop scraping.

# FOR 40K-FT VIEW (STORY & INFRASTRUCTURE) !!
# --- START EDITING-IN ON 1ST TURN ---

# ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs  # <-- ROLLING PIN that gives the 40K foot book-spine view of book-ore (only works for me because of local-only git repo)
# ~/repos/nixos/autognome.py  # <-- You wake up in the morning and your Tooling & Instrumentation folds out of you like Inspector Gadget.
# init.lua                    # <-- Those gadgets are made easy-to-use through nifty keyboard shortcuts (but ya gotta learn vim).
# GLOSSARY.md                 # <-- Like the back of a J.R.R. Tolkien book, there's kooky new terms to know.
# flake.nix                   # <-- Here is my hardware. Here is my state. Put on your sandbox. And please recreate. (Infrastructure as Code / IaC)
# prompt_foo.py               # <-- THIS system
# foo_files.py                # <-- main ROUTER
# requirements.in             # <-- We've "pinned" everything but still want a flexible Python Data Science virtualenv.
# pyproject.toml              # <-- How this is a citizen of the Python "pip install" ecosystem
# __init__.py                 # <-- Version info

# --- END EDITING-IN ON 1ST TURN ---

# scripts/articles/lsa.py     # <-- 2ND BRAIN: Search external memory with `rgx`, `rgxc` & `posts` Blogging for Hackers Jekyll-compatible.

# OPTIONAL ACTUATORS (cheap and good to include to expand the AI's capabilities)
# 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.
# scripts/crawl.py            # <-- Feel free to ask for something to be crawled and included in the next turn.
# scripts/weblogin.py         # <-- Lets the user "warm up" the cache for their web logins at their leisure on a profile that persists.
# scripts/webclip_2_markdown.py  # <-- Surprisingly important program.
 
# MISCELLANEOUS (rare to include but sometimes critical)
# scripts/foo_cartridge.py    # Needs description
# scripts/foo_replay.py       # Needs description
# release.py                  # <-- How everything ends up where it does (GitHub, PyPI, etc.)
# imports/voice_synthesis.py  # <-- The wand can talk to you
# imports/ascii_displays.py   # <-- Where all the ASCII Art lives
# 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.  ---

# Carry-over as the important work-in-progress parts of the project here just
# like above but not as long-standing overarching to the framework but rather
# for the current hot spots actively being worked on.

# --- START THIS DISCUSSION ---

# Get things started here! Guess at what context should be included.
# If you get it wrong, you're just wasting 1-turn because the AI will help.
# Un-comment lines, add lines with absolute-path filenames or `! ` commands. 

# Context 1
# /home/mike/repos/trimnoir/_posts/2026-09-18-the-first-ten-minutes-reproducible-ai-workflows.md
# ! rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-100
# ! rg -n 'ADHOC_CHOP|^PINNED_CHOP = r' flake.nix foo_files.py prompt_foo.py | cut -c1-100
# ! rg -n 'staleness_of|Proceeding anyway|_start_scrape_music|aplay' tools/scraper_tools.py | cut -c1-100
# ! rg -n 'def _narrate|def narrate|speak_text' scripts/mother_cat.py | cut -c1-100
# ! rg -c -w sources flake.nix scripts/boot_menu.py scripts/sources_menu.py assets/installer/mck.sh
# ! curl -fsSL https://pipulate.com/mck.sh | wc -l; wc -l < assets/installer/mck.sh
# scripts/boot_menu.py
# scripts/mother_cat.py
# tools/scraper_tools.py

# Context 2
flake.nix
foo_files.py
prompt_foo.py
scripts/boot_menu.py
scripts/mother_cat.py
tools/scraper_tools.py
scripts/sources_menu.py
apply.py
! .venv/bin/python -m py_compile scripts/boot_menu.py scripts/mother_cat.py tools/scraper_tools.py foo_files.py
! .venv/bin/python scripts/boot_menu.py --recall
! .venv/bin/python scripts/boot_menu.py --recall --all
! PIPULATE_BOOT_MENU=0 .venv/bin/python scripts/boot_menu.py; printf 'env_rc=%s\n' "$?"
! printf '' | .venv/bin/python scripts/boot_menu.py; printf 'pipe_rc=%s\n' "$?"
! rg -n -i 'press 1|three-door|pick a door|select 1|select 2|both servers start' flake.nix scripts/boot_menu.py foo_files.py | cut -c1-120
! rg -n 'sources\(\)|echo "sources:|DOOR_TWO_WORDS|three-door workshop menu|door 2 opens onto|sources panel' flake.nix foo_files.py scripts/boot_menu.py
! rg -n 'WALK_CHOP|--chop WALK_CHOP|Read: \$router|Delivery lane:' flake.nix foo_files.py prompt_foo.py | cut -c1-140
! rg -n 'before_prompt|staleness_of|tick\.wav|ding\.wav|afplay|aplay' tools/scraper_tools.py scripts/mother_cat.py | cut -c1-140
! rg -n 'def _narrate|Make sure your audio is turned on|The page is open|INTRO_NOTICE' scripts/mother_cat.py | cut -c1-140
! nix-instantiate --parse flake.nix >/dev/null

# --- END `adhoc.txt` TEMPLATE ---

Wow I like where this is going and there are some doozie of patches coming up. Itโ€™s nice to get the fresh perspective after all that Claude. Just because ChatGPT speaks more clearly and plainly than that Wogglebug Claude doesnโ€™t mean itโ€™s less smart than Claude. It just means it doesnโ€™t hang its PhD degree on the wall and dance around over-magnified quite as much. And you know what? Gemini is now the plainest, clearest speaker of them all and still gets such a bad rep. Iโ€™ll have to get its opinion on all this.

3: Patches: (the one change between the readings)

Blast Radius Check to establish bisection 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
โœ… WHOLE-FILE WRITE: OVERWROTE 'scripts/boot_menu.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'flake.nix'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'flake.nix'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'flake.nix'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'flake.nix'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'flake.nix'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'flake.nix'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'flake.nix'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'flake.nix'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix) pipulate $ d
diff --git a/flake.nix b/flake.nix
index 79d5e26d..94a9b232 100644
--- a/flake.nix
+++ b/flake.nix
@@ -713,12 +713,12 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           # preserved if/when the directory is transformed into a git repo
           APP_NAME=$(cat whitelabel.txt)
           PROPER_APP_NAME=$(echo "$APP_NAME" | awk '{print toupper(substr($0,1,1)) tolower(substr($0,2))}')
-          # THE BANNER MOVED TO DOOR 1 (2026-08-25). The figlet printed HERE,
-          # ABOVE the threshold, so the human who was one keypress away from
-          # saying "do not start the app" got a full-width ASCII assertion of
-          # that app's identity first. The banner is the curtain going up on
-          # the app; it belongs on the path where the app actually starts, and
-          # it now renders just below the BOOT_CHOICE exit. PROPER_APP_NAME
+          # THE BANNER MOVED BELOW THE THRESHOLD (2026-08-25). The figlet
+          # printed HERE, before the human had chosen whether to start anything,
+          # so the shell-only path got a full-width assertion of the app first.
+          # The banner is the curtain going up on the app; it belongs only on
+          # the path where the app actually starts, just below the BOOT_CHOICE
+          # exit. PROPER_APP_NAME
           # stays computed here -- it is the figlet's only consumer and both
           # live in this one process, so the move strands nothing.
           #
@@ -803,16 +803,16 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           fi
           # Add convenience scripts to PATH
           export PATH="$VIRTUAL_ENV/bin:$PATH"
-          # OUTSIDE THE THRESHOLD BY DESIGN -- these three run on BOTH sides:
+          # OUTSIDE THE THRESHOLD BY DESIGN -- these three run on BOTH paths:
           #   copy_notebook_if_needed -- notebooks on disk are a deliverable of
-          #     the flake, not of the server. Idempotent, cheap, and a door-2
+          #     the flake, not of the server. Idempotent, cheap, and a shell-only
           #     user who later runs `python server.py` should find them there.
           #   pkill -- entering `nix develop` has ALWAYS killed a running
-          #     server. Door 2 keeps that promise instead of inventing a new
-          #     one, so a later `python server.py` finds :5001 free.
+          #     server. The shell-only path keeps that promise instead of
+          #     inventing a new one, so a later `python server.py` finds :5001 free.
           #   git pull -- the `dev` shell skips gitUpdateLogic entirely, so
           #     this bare pull is dev's ONLY update path. Inside the branch, a
-          #     habitual door-2 user would silently drift from upstream and
+          #     habitual shell-only user would silently drift from upstream and
           #     the magic cookie's forever-forward promise would rot.
           copy_notebook_if_needed
           # ONE WORKSHOP AT A TIME, SAID OUT LOUD (2026-08-04). This kill has
@@ -828,14 +828,11 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
             echo "๐Ÿ›‘ Stopped a Pipulate server that was already running (ports 5001/8888 are shared -- one workshop at a time)."
           fi
           git pull --quiet
-          # THE THRESHOLD: two doors, asked BEFORE anything starts.
-          #
-          # boot_menu.py speaks ONLY through its exit code -- 0 start the
-          # app, 10 stay in the shell. Nothing here parses its stdout, so no
-          # capture pipe can ever be held open by it (the rgx/xclip deadlock
-          # is the conviction). That also makes the renderer swappable: a
-          # Textual boot_menu.py can replace the stdlib/Rich one and this
-          # branch never learns the difference.
+          # THE ONE DOOR: on an interactive tty boot_menu.py prints the short
+          # command list and returns 10, so this child exits before either
+          # server starts and the parent shell prompt becomes the introduction.
+          # Automated lanes still return 0: no tty and PIPULATE_BOOT_MENU=0
+          # preserve the pre-menu behavior exactly. Nothing here parses stdout.
           #
           # FALL-THROUGH GUARANTEE: if scripts/boot_menu.py is absent -- an
           # older checkout, a partial clone, a hand-deleted file -- the flake
@@ -847,38 +844,16 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
             BOOT_CHOICE=$?
           fi
           if [ "$BOOT_CHOICE" -eq 10 ]; then
-            # DOOR 2 -- nothing starts. runScript is EXECUTED (named on its
-            # own line in each shellHook), never sourced, so exiting here
-            # ends this child process and drops the parent into its
-            # interactive prompt. That is the same path door 2 already took
-            # under the tail placement, witnessed 2026-07-23 landing at
-            # `(nix) pipulate $`. Exiting BEFORE the JupyterLab launch is
-            # what makes the quiet workshop quiet: no tmux session, no TTS
-            # greeting, no 30-second readiness poll, and -- the visible bug
-            # this fixes -- no backgrounded browser subshell spraying dots
-            # over the prompt and then announcing that a server nobody
-            # authorized failed to start.
+            # Nothing starts. runScript is EXECUTED (named on its own line in
+            # each shellHook), never sourced, so exiting here ends this child
+            # process and drops the parent into its interactive prompt. No tmux
+            # session, TTS greeting, readiness poll or browser-open loop runs.
             exit 0
           fi
-          # DOOR 3 -- the Pipulate tab (2026-09-03). Same servers as door 1,
-          # opposite tab. It assigns the two names the runtime tab shadows
-          # below already read, so the shadows need no new branch and the
-          # .onboarded gate is simply not consulted on this path. A typed
-          # env var loses to a pressed key here, on purpose: the door is
-          # the explicit choice. If boot_menu.py predates door 3 it never
-          # returns 11, and this block is inert.
-          if [ "$BOOT_CHOICE" -eq 11 ]; then
-            PIPULATE_OPEN_JUPYTER=false
-            PIPULATE_OPEN_FASTHTML=true
-          fi
-          # DOOR 1 ONLY: the banner, moved down from above the threshold on
-          # 2026-08-25. Everything below this line runs only when the human
-          # chose to start the app -- or when boot_menu.py is absent and the
-          # FALL-THROUGH GUARANTEE puts us on the pre-menu path, where the
-          # banner printed unconditionally anyway. The full version string
-          # with its description rides here rather than in the readings line
-          # above, because this is the one surface whose entire job is to name
-          # the thing that is starting.
+          # THE START PATH: reached only by an unattended/fail-open entry or by
+          # a word such as `jn` that deliberately re-enters run-script with
+          # PIPULATE_BOOT_MENU=0. The full version string rides here because
+          # this is the surface whose job is to name the thing that is starting.
           figlet "$PROPER_APP_NAME"
           echo "Version: ${version}"
           # THE TAB KNOBS MOVE TO RUNTIME (2026-09-03, smallest possible step
@@ -888,8 +863,8 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           #   PIPULATE_OPEN_JUPYTER=false PIPULATE_OPEN_FASTHTML=true nix develop
           # with no flake edit and no new door. Nothing else changes: both
           # servers still start, and with no env vars set the readings are
-          # byte-identical to before. A future door 3/4 sets these two
-          # variables and nothing below needs to learn about it.
+          # byte-identical to before. Any explicit caller can set these two
+          # variables without teaching the startup body another branch.
           OPEN_JUPYTER="''${PIPULATE_OPEN_JUPYTER:-${autoOpenJupyter}}"
           OPEN_FASTHTML="''${PIPULATE_OPEN_FASTHTML:-${autoOpenFastHTML}}"
           JUPYTER_BROWSER_FLAG=""
@@ -898,7 +873,7 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           # Start JupyterLab in a tmux session
           # NOTE: kill and launch stay PAIRED inside the branch. Splitting
           # them (kill outside, launch inside) would murder another
-          # terminal's JupyterLab every time someone picked door 2.
+          # terminal's JupyterLab every time someone stayed at the shell prompt.
           tmux kill-session -t jupyter 2>/dev/null || true
           # Start JupyterLab with error logging
           tmux new-session -d -s jupyter "source .venv/bin/activate && jupyter lab ${jupyterStartupNotebook} $JUPYTER_BROWSER_FLAG --workspace=\$JUPYTER_WORKSPACE_NAME --NotebookApp.token=\"\" --NotebookApp.password=\"\" --NotebookApp.disable_check_xsrf=True 2>&1 | tee /tmp/jupyter-startup.log"
@@ -1323,7 +1298,7 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           alias gitops='(cd ~/repos/trimnoir && git commit --allow-empty -m "retry" && git push)'
           alias force='(cd ~/repos/trimnoir && git commit --allow-empty -m "retry" && git push)'
           alias isnix="if [ -n \"$IN_NIX_SHELL\" ]; then echo \"โœ“ In Nix shell v${version}\"; else echo \"โœ— Not in Nix shell\"; fi"
-          # THE SECOND DOOR, SPLIT IN TWO (2026-08-25). One word was doing two
+          # THE CONNECTOR ROSTER, SPLIT IN TWO (2026-08-25). One word was doing two
           # unrelated jobs and nothing about the word said so: bare `mcp`
           # listed CONNECTORS that pull material IN, while `mcp <tool>`
           # dispatched a REGISTRY TOOL. Worse, `mcp` is the NAME OF A FILE --
@@ -1332,16 +1307,15 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           # the roster had taken it. Three words now, each of whose bare and
           # argument forms are about the same thing:
           #
-          #   sources        the sources you can name (the roster)
+          #   conn           the connector sources you can name (the roster)
           #   tools          the registry tools you can call
           #   tools <name>   call one
           #   mcp <server>   the real Streamable-HTTP client (aliased below)
           #
-          # Discharges the CONNECTOR ALIAS TRANSITION earmark, whose text asked
-          # only that the roster be renamed so `mcp` came free. `sources` beats
-          # that earmark's own `connect` / `pipe` / `warp` for one reason: it
-          # names the OUTPUT CLASS rather than an action the command does not
-          # perform. The roster connects nothing; `warm` does.
+          # The New-B surface now names the goal rather than the output class:
+          # `conn` is the short word that gets connector sources into context.
+          # The underlying roster file keeps its descriptive filename; there is
+          # deliberately no compatibility alias for the old typed word.
           #
           # FALL-THROUGH GUARANTEE, and it now REFUSES instead of substituting:
           # if the roster file has not landed, say so in one line. The old
@@ -1353,11 +1327,11 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           # FUNCTIONS, NOT ALIASES (THE THREE-TIER AMENDMENT): `tools` needs a
           # branch, and both are typed by a human and never echoed as a `!`
           # probe, so neither needs to be a packaged derivation.
-          sources() {
+          conn() {
             if [ -f "$PIPULATE_ROOT/scripts/sources_menu.py" ]; then
               "$PIPULATE_ROOT/.venv/bin/python" "$PIPULATE_ROOT/scripts/sources_menu.py"
             else
-              echo "sources: scripts/sources_menu.py has not landed in this checkout."
+              echo "conn: scripts/sources_menu.py has not landed in this checkout."
             fi
           }
           tools() {
@@ -1367,23 +1341,18 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
               (cd "$PIPULATE_ROOT" && .venv/bin/python cli.py call "$@")
             fi
           }
-          # THE RECALL WORD. The door-two list prints ONCE, at the moment of
-          # choice, and then scrolls away under whatever the human does next.
-          # `menu` prints it again from the SAME tuple (DOOR_TWO_WORDS in
-          # boot_menu.py), so the reminder can never drift from the original.
+          # THE RECALL WORDS. The short list prints once at shell entry and
+          # scrolls away under whatever the human does next. `menu` recalls the
+          # same SHORT_WORDS tuple; `all` prints the expanded ALL_WORDS tuple.
           #
           # A DISPLAY, NEVER A SECOND THRESHOLD, and the exit code is the whole
           # argument. boot_menu.py FAILS OPEN by design: no tty,
           # PIPULATE_BOOT_MENU=0, or any unexpected exception all return 0,
           # which means START THE APP. That polarity is correct at the
           # threshold, where blocking would strand an unattended nix develop.
-          # At a PROMPT it inverts: a menu that branched on exit 0 would start
-          # a server every time it ran without a tty -- including inside
-          # prompt_foo's `!` executor, where stdout is a pipe, and `pu` would
-          # then pkill the running server, start a new one in the foreground,
-          # and hold the compile until the 180s process-group kill. So nothing
-          # here reads the exit status, and --recall returns before the isatty
-          # gate is ever reached. Door 1 already has a word here: `pu`.
+          # At a PROMPT it inverts: a display wrapper must never branch on that
+          # 0 or a redirected `menu` could start a server. So neither wrapper
+          # reads the exit status; --recall returns before the tty gate.
           #
           # A FUNCTION, not writeShellScriptBin (THE THREE-TIER AMENDMENT):
           # only a human types this, and no child shell needs to resolve it.
@@ -1397,9 +1366,22 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
               echo "menu: scripts/boot_menu.py has not landed in this checkout."
             fi
           }
-          # THE THIRD DOOR: `pu` starts the server door 2 declined to start.
-          # Kill-then-start, so it doubles as a restart and never collides on
-          # :5001 -- the same pkill runScript has always run on shell entry.
+          all() {
+            if [ -f "$PIPULATE_ROOT/scripts/boot_menu.py" ]; then
+              "$PIPULATE_ROOT/.venv/bin/python" "$PIPULATE_ROOT/scripts/boot_menu.py" --recall --all
+            else
+              echo "all: scripts/boot_menu.py has not landed in this checkout."
+            fi
+          }
+          # `jn` is the old full startup path as a word, not a second copy of
+          # that path. Re-enter run-script with the threshold explicitly
+          # declined so JupyterLab and Pipulate start exactly as they do on an
+          # unattended entry, with JupyterLab in front under the current defaults.
+          jn() {
+            (cd "$PIPULATE_ROOT" && PIPULATE_BOOT_MENU=0 ${runScript}/bin/run-script)
+          }
+          # `pu` is the server-only word. Kill-then-start, so it doubles as a
+          # restart and never collides on :5001.
           pu() {
             (
               cd "$PIPULATE_ROOT" || exit 1
@@ -1433,7 +1415,7 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           # and this car rode exactly seven, so promoting it is one more entry
           # in connectorCommands whenever the operator says so.
           # `mcp` NOW MEANS THE CLIENT, which is what the file has always been
-          # named. Freed 2026-08-25 when the roster became `sources`, and
+          # named. Freed 2026-08-25 when the roster moved off `mcp`, and
           # claimed in the SAME car so the word is never a hole. An ALIAS, not
           # a function: nothing branches here, because mcp.py's own bare mode is
           # the branch -- and a bare `mcp` that argparse-exited 2 would have
@@ -1561,7 +1543,19 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           # the dismount, not for this line; bare cpr reads prompt.md when it
           # exists, exactly as bare ahc does.
           epr() { (cd "$PIPULATE_ROOT" && "$(command -v nvim || command -v vim || echo vi)" "$(_walkrouter)"); }
-          cpr() { (cd "$PIPULATE_ROOT" && PIPULATE_ADHOC_FILE="$(_walkrouter)" python prompt_foo.py "$@" --chop ADHOC_CHOP --no-tree); }
+          cpr() {
+            local router="$(_walkrouter)"
+            (
+              cd "$PIPULATE_ROOT" || exit 1
+              PIPULATE_ADHOC_FILE="$router" python prompt_foo.py "$@" --chop WALK_CHOP --no-tree --quiet
+            ) || return $?
+            echo "Read: $router"
+            if [ -n "''${SSH_CLIENT:-}" ]; then
+              echo "Delivery lane: /tmp/clipboard_bridge.txt"
+            else
+              echo "Delivery lane: system clipboard"
+            fi
+          }
           # THE IDEATION DOOR: `idea` compiles IDEATION_CHOP (the constitution's
           # two forcing-function rules) primed for a 30-and-3 / axis-forcing
           # fan-out turn. A function, not an alias, so --profile/--reason pass
@@ -1734,7 +1728,7 @@ runScript = pkgs.writeShellScriptBin "run-script" ''
           # self-contained prompt, then tells the human where to paste it.
           brief() {
             (cd "$PIPULATE_ROOT" && python prompt_foo.py \
-              "You are Yen Sid-ton, the onboarding wizard for Pipulate. A newcomer wants to install Pipulate for the first time. Your FIRST reply must be short and do four things in order: (1) confirm in one line that you hold the full install map (installer, flake, both Pipulate.com pages); (2) show the one-line install command immediately, since it is identical on every OS; (3) ask exactly one question, which OS they are on, noting it changes only the caveats, never the command; (4) add one line noting the command assumes Nix is already installed, and that a nix command not found response means install Nix first and reopen the terminal. From then on: one step per turn, one question maximum per turn, and every step ends with a visible success checkpoint describing what they should literally see (the one-line environment readings, the three-door menu where they press 1, the figlet banner once the app starts, the JupyterLab URL) plus the single most likely failure symptom at that step and its fix. Deliver the macOS --impure exception and the reopen-your-terminal-after-installing-Nix requirement at the moment each can bite, never as an upfront lecture. Offer the magic cookie internals (ZIP + ROT13 key, then git transformation and auto-updates inside nix develop) as an optional aside when relevant or when asked, not as mandatory explanation. When both the server and JupyterLab are confirmed running, declare the install banked and teach the re-entry incantation: cd into the install folder, then nix develop. High signal, low noise. Ask them what they see; never assume." \
+              "You are Yen Sid-ton, the onboarding wizard for Pipulate. A newcomer wants to install Pipulate for the first time. Your FIRST reply must be short and do four things in order: (1) confirm in one line that you hold the full install map (installer, flake, both Pipulate.com pages); (2) show the one-line install command immediately, since it is identical on every OS; (3) ask exactly one question, which OS they are on, noting it changes only the caveats, never the command; (4) add one line noting the command assumes Nix is already installed, and that a nix command not found response means install Nix first and reopen the terminal. From then on: one step per turn, one question maximum per turn, and every step ends with a visible success checkpoint describing what they should literally see (the one-line environment readings, the short command list at the shell prompt, the figlet banner after they type jn, the JupyterLab URL) plus the single most likely failure symptom at that step and its fix. Deliver the macOS --impure exception and the reopen-your-terminal-after-installing-Nix requirement at the moment each can bite, never as an upfront lecture. Offer the magic cookie internals (ZIP + ROT13 key, then git transformation and auto-updates inside nix develop) as an optional aside when relevant or when asked, not as mandatory explanation. When both the server and JupyterLab are confirmed running, declare the install banked and teach the re-entry incantation: cd into the install folder, then nix develop. High signal, low noise. Ask them what they see; never assume." \
               --chop INSTALL_CHOP --no-tree --quiet)
             echo ""
             echo "๐Ÿงž The First Wish is compiled and sitting in your clipboard."
diff --git a/foo_files.py b/foo_files.py
index 421372dc..a7325322 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -1483,8 +1483,8 @@ prompt_foo.py               # <-- THIS system
 # walk                        # <-- Repo-root wrapper; delegates to mck.sh, NOT to scripts/walk.py
 # assets/installer/mck.sh     # <-- THE LAUNCHER (curl -fsSL pipulate.com/mck.sh | bash): finds the workshop, forces the spoken rehearsal on first contact, asks for the word RIDE
 # scripts/mother_cat.py       # <-- THE RIDER (alias: mothercat), Car B: actuates walk.py's validated plan; per-run capture banking, DECANT, the adhocwalk.txt writer
-# scripts/boot_menu.py        # <-- The three-door workshop menu; walk, epr and cpr lead door 2's seven words, the count derived from the tuple
-# scripts/sources_menu.py     # <-- The roster door 2's words print; not a connector executor
+# scripts/boot_menu.py        # <-- The one-door command list; short on shell entry, expanded by `all`, both rendered from tuples in the file
+# scripts/sources_menu.py     # <-- The roster `conn` prints; filename stays descriptive, typed word is shorter
 #
 # AUTHOR
 # scripts/bookmark_import.py  # <-- One bookmark folder -> <name>.walk.md (the human surface) + <name>.exports.sh (the URL values; gitignored by pattern so they never ship)
@@ -1880,7 +1880,7 @@ prompt_foo.py               # <-- THIS system
 # THE ROSTER. Each connector's description below is the file's FIRST
 # DOCSTRING LINE, harvested by ast.get_docstring (deed 1469, re-read at 1472)
 # and the same line sources_menu.py prints, so a wrong sentence here is a
-# wrong sentence in the sources panel: fix the file, then this comment.
+# wrong sentence in the conn roster: fix the file, then this comment.
 # Contract 8 convicted two of them by receipt and one car fixed both
 # (b7f71ffe: gsc.py 73 -> 60 characters; c29eb030: mcp_warm.py a fragment ->
 # one sentence of 57). README.md, noop.py and the dummy server carry
@@ -1888,7 +1888,7 @@ prompt_foo.py               # <-- THIS system
 # (deed 1472): one git mv, one literal sed over every file that named the
 # old path, two Path joins by hand, and exit then nix develop as the
 # ignition, because the eight words on PATH bake the folder into their derivations.
-# scripts/sources_menu.py          # <-- what door 2 opens onto.
+# scripts/sources_menu.py          # <-- what `conn` prints.
 # connectors/README.md     # <-- the contract, the wallet, and the six auth kinds every new connector copies one of
 # connectors/wallet.py     # <-- Connect your accounts, or any site by URL; see what's live.
 # connectors/botify.py     # <-- Bring Botify crawl data and BQL query results into context.
@@ -1987,6 +1987,18 @@ ADHOC_CHOP = r"""
 apply.py
 """
 
+WALK_CHOP = r"""
+# THE WALK ROUTER (only the disposable overlay)
+# COMMAND: PIPULATE_ADHOC_FILE=<router> python prompt_foo.py --chop WALK_CHOP --no-tree --quiet
+# cpr points PIPULATE_ADHOC_FILE at the walk-written router. This chop carries
+# no fixed tail: every payload line between the slot markers comes from that
+# explicit overlay file.
+
+# --- ADHOC SLOT START ---
+# (Structurally empty in the repo. cpr supplies the walk router at compile time.)
+# --- ADHOC SLOT END ---
+"""
+
 PINNED_CHOP = r"""
 # THE PINBOARD HYDRATOR (full text of currently-pinned milestone articles)
 # COMMAND: python prompt_foo.py --chop PINNED_CHOP --no-tree
@@ -2275,7 +2287,7 @@ foo_files.py
 # - TODO (2026-09-16, shadow target scope): exporting PIPULATE_BLOGS_CONFIG for the whole Darwin shell also changes prompt_foo.py's own target-1 view. THIS compile's Recent Git Diff Telemetry rewrote foo_files.py's canonical stats from 1,475 MikeLev.in articles and real Honeybot counts to 0 Mac Shadow articles and blank hydration fields. The formatting goal is met, but compiler telemetry must not dirty shared source with shadow-local stats. Narrow the override to article/corpus commands or give stats an explicit canonical config source; do not fix this by committing the generated zeroes.
 # - TODO (2026-09-16, the walk seam): one real walk after commit 6b10c419 so `cat "$(_walkrouter)"` reads the preview uncommented and the archive commented beneath it; the writer's three branches passed the compile-lane probe, the rider handing it the preview has never run. The Mac is that witness if this machine does not get there first, and needs two nix develop entries, the first pulling these commits and the second running the hook it pulled. MAC READING 2026-09-16: the router does not exist there at all (cat: no such file), because PIPULATE_ADHOC_FILE is unset on that machine and the derivation lands $PIPULATE_ROOT/adhocwalk.txt, gitignored, never written. So the seam is unwitnessed on BOTH machines and the Mac's reading is absence, not staleness -- and `cpr` REFUSES there until a walk or an `epr` write creates the file, which is the fresh-install path a newcomer takes.
 # - TODO (2026-09-16, the question's home): bare cpr reads prompt.md and a newcomer has none; `cpr "question"` works today and costs quoting; the -o split was tried and reverted. Candidates, none chosen: the walk seeds a prompt.md at DECANT, or the router's header teaches the positional. First rule from prompt_foo.py:2843 whether bare cpr with no prompt.md refuses or proceeds (CENSUS: rg -n -A6 'elif os.path.exists\("prompt.md"\)' prompt_foo.py); CENSUS RAN 2026-09-16 (Mac compile): prompt_foo.py:2843 is an elif with NO else -- 2845 blank, 2846 dedents to extra_prompt_parts -- so bare cpr with no prompt.md PROCEEDS and no car is owed for a refusal. STILL OPEN, and six lines cannot see it: proceeds with WHAT, since prompt_content's initializer sits above the window (probe: rg -n -B8). An empty Prompt section is worse than a refusal, because nothing announces it. SEPARATE AND SHARPER: cpr refuses when the ROUTER is missing, which is every fresh install before its first walk.
-# - TODO (2026-09-16, door two): plan is absent from DOOR_TWO_WORDS because _COUNT_WORDS spells up to seven and an eighth word prints a digit; extend the tuple through ten, then add plan after cpr, and the recall probe reads Eight words.
+# - TODO (2026-09-16, command list): plan remains absent from ALL_WORDS. If it earns a newcomer-facing word, add it to the expanded list only; the short list stays the Prompt Fu loop.
 # - TODO (2026-09-16, the editor): the flake pins no editor while plan hardcodes nvim, `alias vim='nvim'` assumes it, and epr, bm, rgx -v fall back nvim, vim, vi; either pin neovim in commonPackages or make every word fall back the same way, and say which in one place.
 # - TODO (2026-09-16, first-walk noise on the Mac): the Chrome version-mismatch auto-heal WARNING prints at every stop, three times per walk, and the Hugging Face download prints six lines around the 60 MB voice model on a first walk; both fire on the ordinary case at the moment a stranger is reading. Sibling of the Missing-phoneme line above.
 # - TODO (2026-09-16, repo root, ON THE LINUX BOX): four deleteme*.txt files and a 35 MB realkeywords.csv sit in THAT repo root, gitignored and not gone (IGNORED IS NOT GONE, .gitignore's own words); delete by hand. NAMING THE MACHINE IS THE POINT: the same ls on the Mac 2026-09-16 read "No such file or directory" for both, which is true and discharges nothing, because gitignored files never travel. A debt that does not name its machine gets closed by a probe on the other one.
@@ -2296,8 +2308,7 @@ foo_files.py
 # - TODO (2026-09-15): one public_walk ride left 6 non-curl /walk/ lines in npvg.access.log, two per page; the user-agent split (deed 1408) read ONE agent, Chrome/150 on X11, with one 200 and one 304 per page, so there is no second fetcher: the same browser loaded each page twice and the second load was a conditional revalidation. Read the six lines in log order (time, status, path, no address) to rule between a double load inside one stop and a restore of the previous tab when the next stop's browser launches; count no rides until that is ruled, and filter the operator's own rides by address before reading the funnel at all.
 
 
-# - TODO (2026-09-15, operator ruling): replace jeopardy.wav under the summoning with free-licensed or public-domain audio, a ticking clock while the browser waits and an egg-timer ding at the end of the wait.
-# - TODO (2026-09-15, next door): the walk door in scripts/boot_menu.py, with its own exit code and runScript branch so exit 0 keeps meaning start the app and Enter keeps starting it, plus one label sweep across boot_menu.py, the flake's voice greeting and the brief prompt; ride it after the DECANT ride.
+# - TODO (2026-09-15, operator ruling; mechanism landed with THE ONE DOOR): supply copyright-free tick.wav and ding.wav under ~/.local/share/pipulate/. The scraper now uses them when present, afplay on Darwin/aplay elsewhere, and stays silent when absent; no wav ships in git.
 # - TODO (2026-09-13): npvg.org rides mikelev.in's public address as a STATIC A record while only mikelev.in gets the namecheap-ddns heartbeat; a second unit (domain=npvg.org, its own token) or an ALIAS record is owed before the next public-IP change, or the pad goes dark with nothing printed anywhere.
 # - TODO (2026-09-13): the pad has no favicon, so every browser visit writes a 404 line into npvg.access.log beside its 200 (the cellular witness was two lines for one visit); a locations."= /favicon.ico" returning 204 is three lines, owed before the funnel is read from that log.
 # - EARMARK: THE DIRECTORY KEY CROSSED MACHINES (banked 2026-09-08, convicted by the second machine): a shared file may not name a thing by a MACHINE-LOCAL handle, because the handle is assigned per machine and the file is not. Chrome numbers profile directories in creation order, so "Profile 2" was the Work profile on NixOS and the PERSONAL profile on the Mac (Mike, 121 synced bookmarks), and bookmarks.nix, keyed by that directory, resolved to the wrong profile the first time a second checkout ran it; only the sync_metadata fence stood between a real run and a harvest-and-wipe of a personal bar. The key had been right for the whole life of the feature on the one machine that existed, which is SINGLE-CANDIDATE BLINDNESS wearing a filename: n=1 cannot tell a stable identity from a coincidence. CURE, a grammar change rather than a value change: the key is a human name and a `match` list names IDENTITIES the machine derives and the operator cannot author (a label, an account, an account domain, read from the application's own registry); exactly one hit resolves, none skips, several refuse. THE TEST before any key rides a shared file: is this value assigned by the machine in an order the other machine never saw? Directory numbers, pids, device paths, X display numbers and "Profile N" all fail it. Sibling of THE DERIVED-PATH RULE (a write target computed from an identity the writer cannot author) and of SINGLE-CANDIDATE BLINDNESS (create the second candidate); this names the class of key the second candidate convicts.
diff --git a/scripts/boot_menu.py b/scripts/boot_menu.py
index 9c8e2bf9..a74b5897 100644
--- a/scripts/boot_menu.py
+++ b/scripts/boot_menu.py
@@ -1,262 +1,74 @@
 #!/usr/bin/env python3
 """
-boot_menu.py โ€” the threshold at the end of `nix develop`.
+boot_menu.py โ€” the one-door threshold at the end of `nix develop`.
 
-Three doors, one keypress:
-  [1] JupyterLab tab     both servers start; JupyterLab opens in the browser (today's default)
-  [2] Text Commands      NOTHING starts; `walk`, `epr`, `cpr`, `sources`, `brief`, `pu`, `menu` wait at the prompt
-  [3] Pipulate tab       both servers start; the app opens in the browser instead of JupyterLab
+An interactive terminal no longer asks the newcomer to choose a numbered door.
+It prints the short command list and returns 10, which tells flake.nix to stop
+before either server starts and hand the human the shell prompt. `menu` recalls
+the short list; `all` recalls the expanded list.
 
-THE PROTOCOL IS THE EXIT CODE, never stdout. Nothing parses this program's
-output, so no capture pipe can ever be held open by it (the rgx/xclip
-deadlock of 2026-07 is the conviction). That also makes the renderer
-swappable: a Textual version can replace this one without flake.nix
-learning anything.
+THE PROTOCOL IS THE EXIT CODE, never stdout:
 
-  0   start the app       (non-tty, PIPULATE_BOOT_MENU=0, opt-in timeout)
-  10  drop to the Nix CLI (also: Ctrl+C, Esc, q)
-  11  start the app, Pipulate tab in front (flake maps this onto the tab shadows)
+  0   start the app       (non-tty or PIPULATE_BOOT_MENU=0)
+  10  drop to the Nix CLI after printing the short list
 
-FAIL-OPEN BY CONSTRUCTION. Every path an automated caller can reach -- no
-tty, no termios, PIPULATE_BOOT_MENU=0, unexpected exception -- returns 0,
-exactly what the shell did before this file existed. A menu that can
-strand an automated `nix develop` (autognome's Desktop 7 tab, CI, an
-SSH session without a tty) is strictly worse than no menu at all.
+FAIL-OPEN BY CONSTRUCTION. Every unattended path keeps the pre-menu behavior:
+no tty, PIPULATE_BOOT_MENU=0, or an unexpected display failure returns 0. The
+interactive path blocks on nothing and reads no keys; the terminal itself is
+the introduction.
 
-WAITING FOREVER IS SAFE only because it happens strictly AFTER the isatty
-gate: a human is provably present before anything blocks. The one
-unattended tty in this world -- autognome's Desktop 7 "Pipulate Server"
-tab -- declares intent with PIPULATE_BOOT_MENU=0 and never reaches the
-select loop at all.
-
-Stdlib only. Rich is used when importable and never required.
+Stdlib only.
 
 Env:
-  PIPULATE_BOOT_MENU=0            skip the menu entirely, start the app
-  PIPULATE_BOOT_MENU_TIMEOUT=10   opt back in to a countdown (default: none)
+  PIPULATE_BOOT_MENU=0   skip the list and start the app
 """
 import os
-import select
 import sys
-import time
-from pathlib import Path
 
 EXIT_START = 0
 EXIT_SHELL = 10
-# DOOR 3 (2026-09-03): same servers as door 1, opposite tab. The flake reads
-# this code and sets PIPULATE_OPEN_JUPYTER=false / PIPULATE_OPEN_FASTHTML=true
-# before its runtime tab shadows resolve. An older flake does not know 11 and
-# falls through to door-1 behavior, so this can never strand anyone.
-EXIT_PIPULATE = 11
-
-# No deadline by default. A countdown here protects nothing: the only
-# unattended tty is opted out with PIPULATE_BOOT_MENU=0, so every caller
-# reaching the select loop has a human in front of it. Convicted
-# 2026-07-23 -- the panel rendered, ten seconds elapsed underneath a
-# dot-spew from the browser-poll subshell, and the server started unasked.
-DEFAULT_TIMEOUT = None
 
-# Enter and a few mnemonics all mean "go". Unlisted keys are ignored, so a
-# fat finger never picks a door for you.
-START_KEYS = {"1", "\r", "\n", "y", "Y", "s", "S"}
-SHELL_KEYS = {"2", "q", "Q", "n", "N", "l", "L", "\x03", "\x04"}
-PIPULATE_KEYS = {"3", "p", "P"}
-# THE DOOR-2 VOCABULARY, AND ITS COUNT DERIVED FROM IT (2026-08-26). Two
-# strings in this file used to claim how many words wait at this prompt --
-# the panel row and the list heading -- coupled only by somebody remembering
-# they were coupled. That coupling has ALREADY been missed by an instrument:
-# an rg probe for 'Three words to start from' found the heading and was
-# structurally blind to 'three words wait at the prompt', because the second
-# string spells the identical claim differently. A count that nothing
-# computes is a claim that drifts, so nothing computes it by hand here
-# either. Both surfaces read this tuple; the next word costs one line and
-# cannot lie. (The module docstring above still names the words in prose --
-# that one is a description for a reader, not a count, and it moves under
-# the SAME-CAR LABEL RULE like any other label.)
-# ORDER IS A SCOPE LADDER, neither alphabetical nor arbitrary: be carried
-# (walk) -> edit what the walk wrote (epr) -> compile it for an AI (cpr) ->
-# look around here yourself (sources) -> hand it to someone elsewhere
-# (brief) -> reverse the choice you just made (pu). `walk` leads because it
-# is the only row that asks nothing of you first. `menu` rides
-# LAST and is not a rung on that ladder at all: it is not a place to go, it
-# is how you get this list back after it has scrolled away. Its row is also
-# the only thing that tells a newcomer the word exists -- a recall command
-# nothing announces is a command nobody types.
-# No row spells the brand any more, so the .format(name=...) call at the print
-# site currently no-ops. It stays: a future row may need it, and a row that
-# silently printed a literal {name} would be worse than a call that does
-# nothing.
-DOOR_TWO_WORDS = (
-    ("walk", "take the guided tour -- public pages, nothing to log into"),
-    # epr and cpr joined 2026-09-16: the two words after the walk. Seven is
-    # the last count _COUNT_WORDS spells; an eighth word prints a digit.
-    ("epr", "edit the prompt router -- the list the walk wrote of what an AI should see, and your question"),
-    ("cpr", "compile the prompt router into your clipboard -- paste it into an AI"),
-    ("sources", "see what this shell can reach outside this machine"),
-    ("brief", "compile this workshop into your clipboard for an AI -- the context compiler's first job"),
-    ("pu", "change your mind and start the app server after all"),
-    ("menu", "print this list again once it scrolls away"),
+SHORT_WORDS = (
+    ("menu", "print this list again (useful once it scrolls away)"),
+    ("walk", "take guided tour of context compiler (recommended)"),
+    ("conn", "get mcp, jira, email, docs, etc. into context"),
+    ("epr", "edit prompt router (after the walk)"),
+    ("cpr", "compile prompt router (paste results into chatbot)"),
+    ("all", "expanded menu"),
 )
-# Spelled out because "four words wait" reads better than "4 words wait".
-# The digit fallback means a word count past seven degrades to something
-# true rather than to an IndexError in a menu.
-_COUNT_WORDS = ("no", "one", "two", "three", "four", "five", "six", "seven")
-def _count_word(n):
-    """Spell a small count; fall back to the digit rather than guessing."""
-    return _COUNT_WORDS[n] if 0 <= n < len(_COUNT_WORDS) else str(n)
-
-
-def print_door_two_words(name):
-    """Print the door-two list. ONE implementation, TWO call sites.
-
-    main() prints it at the moment of choice; `menu` prints it again later
-    through --recall. Both read the same tuple, so the reminder cannot drift
-    from the thing it reminds you of -- which is the whole reason
-    DOOR_TWO_WORDS is data rather than two hand-written strings.
-    """
-    print(_count_word(len(DOOR_TWO_WORDS)).capitalize() + " words to start from:")
-    width = max(len(word) for word, _ in DOOR_TWO_WORDS)
-    for word, description in DOOR_TWO_WORDS:
-        print("  " + word.ljust(width) + "   " + description.format(name=name))
-
-
-def _app_name(root: Path) -> str:
-    """Mirror runScript's awk: capitalize first char, lowercase the rest."""
-    try:
-        raw = (root / "whitelabel.txt").read_text(encoding="utf-8").strip()
-    except OSError:
-        raw = ""
-    raw = raw or "Pipulate"
-    return raw[:1].upper() + raw[1:].lower()
-
-
-def _timeout():
-    """Seconds before the default fires, or None to wait for the human.
-
-    Missing, empty, unparseable, and non-positive values all resolve to the
-    same answer -- no deadline -- so a typo can never silently install a
-    countdown nobody asked for. PIPULATE_BOOT_MENU_TIMEOUT=N opts one back in.
-    """
-    raw = os.environ.get("PIPULATE_BOOT_MENU_TIMEOUT", "").strip()
-    if not raw:
-        return DEFAULT_TIMEOUT
-    try:
-        value = float(raw)
-    except (TypeError, ValueError):
-        return DEFAULT_TIMEOUT
-    return value if value > 0 else DEFAULT_TIMEOUT
-
-
-def _render(name, seconds) -> None:
-    # THE BRAND IS NOT THE DOOR (2026-08-28). Row one said "Start <AppName>"
-    # to a reader who has typed exactly one command in their life -- `nix
-    # develop` -- and therefore has no referent for the name. It named the
-    # SYSTEM where the reader needed the OUTCOME. JupyterLab is what visibly
-    # opens; the name is revealed after the choice, and again by the figlet
-    # runScript prints on the door-1 path only.
-    # `name` IS RETAINED AND CURRENTLY UNUSED HERE, deliberately: it keeps
-    # this signature callable as a non-blocking probe
-    # (_render("Pipulate", None) renders the panel without waiting on a
-    # keypress, which main() cannot do), and a whitelabel install that wants
-    # its own name back in the title is then a one-string edit rather than a
-    # signature change.
-    lines = [
-        "[1]  JupyterLab tab     both servers start; JupyterLab opens in the browser",
-        f"[2]  Text Commands      nothing starts -- {_count_word(len(DOOR_TWO_WORDS))} words wait at the prompt",
-        "[3]  Pipulate tab       both servers start; the app opens in the browser",
-    ]
-    if seconds is None:
-        subtitle = "waiting for your choice -- Ctrl+C also drops to the shell"
-    else:
-        subtitle = f"no keypress in {seconds:.0f}s opens door 1"
-    try:
-        from rich.console import Console
-        from rich.panel import Panel
 
-        Console().print(
-            Panel(
-                "\n".join(lines),
-                # NOT "Linux": this flake is eachDefaultSystem and carries
-                # live Darwin branches, so a title claiming Linux is false on
-                # every Mac and invisible to the one person who could fix it.
-                # "*nix" is true on Linux, WSL, and certified-UNIX macOS
-                # alike, and echoing the command they just typed is the whole
-                # education this line owes a first-timer.
-                title="nix develop -- a reproducible *nix shell :: pick a door",
-                subtitle=subtitle,
-                border_style="cyan",
-                padding=(1, 2),
-            )
-        )
-    except Exception:
-        print()
-        print("--- nix develop -- a reproducible *nix shell :: pick a door ---")
-        for line in lines:
-            print("  " + line)
-        print(f"  ({subtitle})")
-        print()
-
-
-def _read_choice(seconds) -> int:
-    """One raw keypress, optionally against a deadline.
+ALL_WORDS = (
+    ("menu", "print the shorter list"),
+    ("walk", "take guided tour of context compiler (recommended)"),
+    ("conn", "get mcp, jira, email, docs, etc. into context"),
+    ("epr", "edit prompt router (after the walk)"),
+    ("cpr", "compile prompt router (paste results into chatbot)"),
+    ("all", "print this expanded list again"),
+    ("brief", "compile this workshop into your clipboard for an AI"),
+    ("jn", "start JupyterLab and Pipulate, JupyterLab first"),
+    ("pu", "start the Pipulate server"),
+)
 
-    seconds=None blocks in select() until a key arrives. termios is restored
-    unconditionally either way.
-    """
-    import termios
-    import tty
 
-    fd = sys.stdin.fileno()
-    saved = termios.tcgetattr(fd)
-    deadline = None if seconds is None else time.monotonic() + seconds
-    try:
-        tty.setraw(fd)
-        while True:
-            remaining = None
-            if deadline is not None:
-                remaining = deadline - time.monotonic()
-                if remaining <= 0:
-                    return EXIT_START
-            ready, _, _ = select.select([fd], [], [], remaining)
-            if not ready:
-                return EXIT_START
-            key = os.read(fd, 1).decode("utf-8", "replace")
-            if key == "\x1b":
-                # Arrow and function keys arrive ESC-prefixed. Drain the tail
-                # and keep waiting so a stray arrow never picks a door; a
-                # LONE Esc is a deliberate "give me the shell".
-                if select.select([fd], [], [], 0.05)[0]:
-                    os.read(fd, 8)
-                    continue
-                return EXIT_SHELL
-            if key in START_KEYS:
-                return EXIT_START
-            if key in SHELL_KEYS:
-                return EXIT_SHELL
-            if key in PIPULATE_KEYS:
-                return EXIT_PIPULATE
-    finally:
-        termios.tcsetattr(fd, termios.TCSADRAIN, saved)
+def print_words(words) -> None:
+    """Print one command list from data; short and expanded cannot drift."""
+    print("type one:")
+    width = max(len(word) for word, _ in words)
+    for word, description in words:
+        print("  " + word.ljust(width) + "  " + description)
 
 
 def main() -> int:
-    # THE RECALL PATH, AND WHY IT SITS ABOVE EVERY GATE. The door-two list
-    # prints once and then scrolls away under whatever the human does next;
-    # flake.nix's `menu` calls this to print it again. It runs BEFORE the
-    # PIPULATE_BOOT_MENU and isatty gates deliberately: those exist to keep an
-    # unattended `nix develop` from blocking at a threshold, and a recall
-    # blocks on nothing, so inheriting their fail-open would only make the
-    # word print nothing in a pipe and nothing under PIPULATE_BOOT_MENU=0.
-    #
-    # IT EXITS 0 AS AN ORDINARY SUCCESSFUL DISPLAY, which COLLIDES with
-    # EXIT_START. No caller may branch on the exit code of a --recall run, and
-    # the flake wrapper deliberately does not: a wrapper that read 0 as "start
-    # the app" would launch a server every time `menu` ran without a tty.
-    if "--recall" in sys.argv[1:]:
-        root = Path(os.environ.get("PIPULATE_ROOT") or Path(__file__).resolve().parent.parent)
+    args = set(sys.argv[1:])
+
+    # Recall is a display, not the startup threshold. It deliberately runs
+    # before the unattended gates so `menu` and `all` also work when stdout is
+    # redirected or PIPULATE_BOOT_MENU=0 is inherited by the shell.
+    if "--recall" in args:
         print()
-        print_door_two_words(_app_name(root))
+        print_words(ALL_WORDS if "--all" in args else SHORT_WORDS)
         return 0
+
     if os.environ.get("PIPULATE_BOOT_MENU", "1").strip().lower() in {
         "0",
         "no",
@@ -268,29 +80,12 @@ def main() -> int:
     if not (sys.stdin.isatty() and sys.stdout.isatty()):
         return EXIT_START
 
-    root = Path(os.environ.get("PIPULATE_ROOT") or Path(__file__).resolve().parent.parent)
-    name = _app_name(root)
-    seconds = _timeout()
-
-    _render(name, seconds)
-
     try:
-        choice = _read_choice(seconds)
-    except KeyboardInterrupt:
-        choice = EXIT_SHELL
-    except Exception:
-        choice = EXIT_START
-
-    print()
-    if choice == EXIT_SHELL:
-        print("[2] Text Commands. Nothing started -- no JupyterLab, no server.")
         print()
-        print_door_two_words(name)
-    elif choice == EXIT_PIPULATE:
-        print(f"[3] Starting {name} -- the app tab will open when the server answers.")
-    else:
-        print(f"[1] Starting {name}...")
-    return choice
+        print_words(SHORT_WORDS)
+    except Exception:
+        return EXIT_START
+    return EXIT_SHELL
 
 
 if __name__ == "__main__":
(nix) pipulate $ m
๐Ÿ“ Committing: chore: Refactor boot_menu.py for clearer interaction and fail-open behavior
[main ec707d57] chore: Refactor boot_menu.py for clearer interaction and fail-open behavior
 3 files changed, 140 insertions(+), 340 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'scripts/mother_cat.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'scripts/mother_cat.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'scripts/mother_cat.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'scripts/mother_cat.py'.
(nix) pipulate $ d
diff --git a/scripts/mother_cat.py b/scripts/mother_cat.py
index d10da2b3..fedc446d 100644
--- a/scripts/mother_cat.py
+++ b/scripts/mother_cat.py
@@ -47,22 +47,24 @@ if str(REPO_ROOT) not in sys.path:
     sys.path.insert(0, str(REPO_ROOT))
 
 
-def _narrate(text, disclosed, indent="  "):
+def _narrate(text, disclosed):
     """Speak scripted guidance if Piper is available; never gate the ride.
 
-    indent is the printed line's left margin. Two spaces under a stop header
-    read as nesting; the walk's opening sentence sits at the top level, wraps,
-    and an indented first line over flush continuations reads as a mistake
-    (operator, 2026-09-17), so that call passes none.
+    The visible channel always lands first, before importing or invoking Piper.
+    A blank line supplies separation without an indent that disappears when an
+    eighty-column terminal wraps the guidance.
     """
+    print()
+    print(text)
+
     try:
         from imports.voice_synthesis import chip_voice_system
     except Exception as exc:
-        print(f"{indent}(voice import unavailable: {exc}) {text}")
+        print(f"(voice import unavailable: {exc})")
         return disclosed
 
     if chip_voice_system is None:
-        print(f"{indent}(voice unavailable) {text}")
+        print("(voice unavailable)")
         return disclosed
 
     # THE NARRATION VANISHED WITH ITS OWN ERROR (convicted 2026-08-02, ride
@@ -72,7 +74,6 @@ def _narrate(text, disclosed, indent="  "):
     # the ride delivered NEITHER audio NOR words -- the NARRATE beat of the
     # kata became a silent no-op that reported success. Print first, then
     # speak, so the visible channel never depends on the audible one failing.
-    print(f"{indent}{text}")
     try:
         if not disclosed:
             result = chip_voice_system.speak_text(
@@ -99,7 +100,7 @@ def _narrate(text, disclosed, indent="  "):
                 f"{result.get('error', 'unknown error')})"
             )
     except Exception as exc:
-        print(f"{indent}(voice error, continuing: {exc}) {text}")
+        print(f"(voice error, continuing: {exc})")
 
     return disclosed
 
@@ -710,8 +711,8 @@ def _announce_consent(trail_path, intro=False):
     """Describe capture and handoff, never grant authorization here.
 
     Custom walks use the same trail projection as walk_cartridge.
-    The bundled introduction uses the shared plain-language contract;
-    --intro is validated separately before any narration or capture.
+    The launcher owns the bundled plain-language INTRO_NOTICE; --intro avoids
+    printing it a second time and adds only the local summary/check lines.
     """
     try:
         surface = walk_cartridge._derive_consent_surface(trail_path.read_bytes())
@@ -723,7 +724,7 @@ def _announce_consent(trail_path, intro=False):
         print(f"  (consent surface unavailable: {exc})")
         return
     if intro:
-        print("\n" + INTRO_NOTICE)
+        print()
         print(f"Summary file: {DECANT_PREVIEW_PATH.relative_to(REPO_ROOT)} (private; replaced on save).")
         print("Checks can miss private details. A blocked check leaves the older file alone.\n")
         return
@@ -899,6 +900,7 @@ async def _ride_steps(trail_path, archive, dry_narrate=False, exports_path=None,
     # THE VOICE ASKS FIRST (2026-09-17), before the description, before the
     # practice notice below says nothing needs typing, and in practice mode
     # too, because the rehearsal is the first thing a newcomer hears.
+    print("Make sure your audio is turned on.\n")
     _ask_voice()
     # THE DESCRIPTION SPEAKS FIRST (2026-09-05). walk.py has validated
     # trail.description as non-empty since Car A, and nothing read it at
@@ -911,7 +913,7 @@ async def _ride_steps(trail_path, archive, dry_narrate=False, exports_path=None,
     rehearsal = "Practice only. In the real walk: " if dry_narrate else ""
     if dry_narrate:
         print("Practice only. No pages will open. You do not need to type anything.\n")
-    disclosed = _narrate(rehearsal + trail["description"], False, indent="")
+    disclosed = _narrate(rehearsal + trail["description"], False)
     _announce_consent(trail_path, intro=intro)
     captured = []
     skipped = archive.setdefault("skipped", [])
(nix) pipulate $ m
๐Ÿ“ Committing: refactor: remove unnecessary indenting in _narrate
[main 784a3918] refactor: remove unnecessary indenting in _narrate
 1 file changed, 15 insertions(+), 13 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'scripts/mother_cat.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'tools/scraper_tools.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'tools/scraper_tools.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'tools/scraper_tools.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'tools/scraper_tools.py'.
(nix) pipulate $ d
diff --git a/scripts/mother_cat.py b/scripts/mother_cat.py
index fedc446d..e0601111 100644
--- a/scripts/mother_cat.py
+++ b/scripts/mother_cat.py
@@ -953,10 +953,18 @@ async def _ride_steps(trail_path, archive, dry_narrate=False, exports_path=None,
                 ) from exc
 
         params = walk._browser_params(url, trail["defaults"])
+        def checkpoint_narration():
+            nonlocal disclosed
+            disclosed = _narrate(
+                "The page is open. When it looks finished, come back to the terminal and type CAPTURE.",
+                disclosed,
+            )
+
         result = await guided_browser_capture(
             params,
             stdin=sys.stdin,
             stdout=sys.stdout,
+            before_prompt=checkpoint_narration,
         )
 
         if not result.get("success"):
diff --git a/tools/scraper_tools.py b/tools/scraper_tools.py
index e93fd0f0..32b1c2cc 100644
--- a/tools/scraper_tools.py
+++ b/tools/scraper_tools.py
@@ -235,7 +235,7 @@ def _document_candidates(cdp_events: list, domain: str, final_url: str = "") ->
     return on_host
 
 
-def _capture_checkpoint(stdin=None, stdout=None) -> dict:
+def _capture_checkpoint(stdin=None, stdout=None, before_prompt=None) -> dict:
     """Require an explicit CAPTURE token from the human's keyboard.
 
     /DEV/TTY PREFERENCE (convicted 2026-07-29, public_walk stop 1): the
@@ -277,8 +277,16 @@ def _capture_checkpoint(stdin=None, stdout=None) -> dict:
             }
 
         try:
+            if before_prompt is not None:
+                try:
+                    before_prompt()
+                except Exception as exc:
+                    output_stream.write(f"\n(checkpoint narration unavailable: {exc})\n")
+            else:
+                output_stream.write(
+                    "\nWhen the page you want is ready, type CAPTURE and press Enter.\n"
+                )
             output_stream.write(
-                "\nWhen the page you want is ready, type CAPTURE and press Enter.\n"
                 "Any other response aborts without capturing artifacts.\n"
                 "CAPTURE> "
             )
@@ -972,7 +980,7 @@ async def selenium_automation(params: dict) -> dict:
     return await _selenium_capture(params)
 
 
-async def guided_browser_capture(params: dict, stdin=None, stdout=None) -> dict:
+async def guided_browser_capture(params: dict, stdin=None, stdout=None, before_prompt=None) -> dict:
     """Capture one human-guided page through one visible persistent driver."""
     if not isinstance(params, dict):
         return {
@@ -1045,5 +1053,6 @@ async def guided_browser_capture(params: dict, stdin=None, stdout=None) -> dict:
         checkpoint=lambda: _capture_checkpoint(
             stdin=input_stream,
             stdout=stdout,
+            before_prompt=before_prompt,
         ),
     )
(nix) pipulate $ m
๐Ÿ“ Committing: chore: Update `mother_cat.py` and `scraper_tools.py` for checkpoint narration
[main 64088435] chore: Update `mother_cat.py` and `scraper_tools.py` for checkpoint narration
 2 files changed, 20 insertions(+), 3 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'tools/scraper_tools.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'tools/scraper_tools.py'.
(nix) pipulate $ d
diff --git a/tools/scraper_tools.py b/tools/scraper_tools.py
index 32b1c2cc..1f3655ce 100644
--- a/tools/scraper_tools.py
+++ b/tools/scraper_tools.py
@@ -616,20 +616,25 @@ async def _selenium_capture(params: dict, checkpoint=None) -> dict:
         logger.info(f"Navigating to: {url}")
         driver.get(url)
 
-        try:
-            if verbose: logger.info("Waiting for security challenge to trigger a reload (Stage 1)...")
-            initial_body = driver.find_element(By.TAG_NAME, 'body')
-            WebDriverWait(driver, 20).until(EC.staleness_of(initial_body))
-            if verbose: logger.success("โœ… Page reload detected!")
-            
-            if verbose: logger.info("Waiting for main content to appear after reload (Stage 2)...")
-            WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "body")))
-            if verbose: logger.success("โœ… Main content located!")
-        except Exception as e:
-            if verbose: logger.info(f"Did not detect a page reload for security challenge. Proceeding anyway.")
+        # A human-guided ride already has the CAPTURE fence as its settle
+        # detector. The 20-second staleness wait exists for unattended sigil
+        # scrapes, where a security interstitial may reload underneath us.
+        if not interactive:
+            try:
+                if verbose: logger.info("Waiting for security challenge to trigger a reload (Stage 1)...")
+                initial_body = driver.find_element(By.TAG_NAME, 'body')
+                WebDriverWait(driver, 20).until(EC.staleness_of(initial_body))
+                if verbose: logger.success("โœ… Page reload detected!")
+
+                if verbose: logger.info("Waiting for main content to appear after reload (Stage 2)...")
+                WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, "body")))
+                if verbose: logger.success("โœ… Main content located!")
+            except Exception:
+                if verbose: logger.info("Did not detect a page reload for security challenge. Proceeding anyway.")
 
-        # The wait is over one way or the other: cut the think-music sharply.
-        _stop_scrape_music(music_proc)
+        # The page-load phase is over: stop ticking and, if the operator
+        # supplied one, play the ready sound exactly once.
+        _stop_scrape_music(music_proc, ding=True)
         music_proc = None
 
         if checkpoint is not None:
@@ -761,8 +766,8 @@ async def _selenium_capture(params: dict, checkpoint=None) -> dict:
             # ledger-only so it can be replayed offline over any cached capture.
             #
             # [-1] SURVIVES, and its rationale is still UNWITNESSED: the
-            # staleness_of() wait above exists because a security challenge
-            # serves an interstitial Document and then reloads, making the LAST
+            # non-interactive staleness_of() wait above exists because a security
+            # challenge serves an interstitial Document and then reloads, making the LAST
             # candidate the real page. No capture on this machine has ever been
             # challenged -- every scrape logs "Did not detect a page reload."
             # The reasoning is sound from the code and has no receipt behind it.
(nix) pipulate $ m
๐Ÿ“ Committing: chore: Improve security challenge reload handling in _selenium_capture
[main 9e035432] chore: Improve security challenge reload handling in _selenium_capture
 1 file changed, 20 insertions(+), 15 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'tools/scraper_tools.py'.
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'tools/scraper_tools.py'.
(nix) pipulate $ d
diff --git a/tools/scraper_tools.py b/tools/scraper_tools.py
index 1f3655ce..f8da224d 100644
--- a/tools/scraper_tools.py
+++ b/tools/scraper_tools.py
@@ -3,6 +3,7 @@ import asyncio
 import hashlib
 import json
 import os
+import shlex
 import sys
 import shutil
 import tempfile
@@ -71,35 +72,38 @@ def _guided_path_component(url: str) -> tuple[str, str]:
 
 
 # --- Optional audio during browser startup ---
-# Forked from stream.py's start_updating_music/stop_updating_music pattern:
-# a marker-tagged shell loop in its OWN process group (os.setsid), killed as
-# a group, with an idempotent pkill backstop keyed to the marker so it can
-# never touch any other aplay pipeline. The wav lives in repo negative space
-# (gitignored) or ~/.local/share/pipulate/; absent file = silent no-op.
+# Operator-owned courtesy sounds live outside the repo. tick.wav loops while
+# the browser is opening; ding.wav plays once when the page-load phase ends.
+# No file means no sound. Linux uses aplay; Darwin uses afplay.
 SCRAPE_MUSIC_MARKER = "pipulate-scrape-music"
+SOUND_DIR = Path.home() / ".local/share/pipulate"
 
 
-def _find_music_file():
-    candidates = [
-        Path(__file__).resolve().parent.parent / "jeopardy.wav",
-        Path.home() / ".local/share/pipulate/jeopardy.wav",
-        Path.home() / ".local/share/honeybot/jeopardy.wav",
-    ]
-    for c in candidates:
-        if c.exists():
-            return c
-    return None
+def _sound_file(name):
+    path = SOUND_DIR / name
+    return path if path.is_file() else None
+
+
+def _player_args(sound):
+    if sound is None:
+        return None
+    if sys.platform == "darwin":
+        player = shutil.which("afplay")
+        return [player, str(sound)] if player else None
+    player = shutil.which("aplay")
+    return [player, "-q", "-D", "default", str(sound)] if player else None
 
 
 def _start_scrape_music(verbose=True):
     import subprocess
-    music = _find_music_file()
-    if not music:
-        return None
 
+    args = _player_args(_sound_file("tick.wav"))
+    if not args:
+        return None
+    command = " ".join(shlex.quote(part) for part in args)
     try:
         return subprocess.Popen(
-            ["sh", "-c", f'while :; do aplay -q -D default "{music}"; done # {SCRAPE_MUSIC_MARKER}'],
+            ["sh", "-c", f"while :; do {command}; done # {SCRAPE_MUSIC_MARKER}"],
             stdout=subprocess.DEVNULL,
             stderr=subprocess.DEVNULL,
             preexec_fn=os.setsid,
@@ -108,18 +112,36 @@ def _start_scrape_music(verbose=True):
         return None
 
 
-def _stop_scrape_music(proc):
+def _stop_scrape_music(proc, ding=False):
     import signal
     import subprocess
+
     if proc is not None:
         try:
             os.killpg(os.getpgid(proc.pid), signal.SIGKILL)
         except Exception:
             pass
     try:
-        subprocess.run(["pkill", "-f", SCRAPE_MUSIC_MARKER], check=False)
+        subprocess.run(
+            ["pkill", "-f", SCRAPE_MUSIC_MARKER],
+            check=False,
+            stdout=subprocess.DEVNULL,
+            stderr=subprocess.DEVNULL,
+        )
     except Exception:
         pass
+    if ding:
+        args = _player_args(_sound_file("ding.wav"))
+        if args:
+            try:
+                subprocess.run(
+                    args,
+                    check=False,
+                    stdout=subprocess.DEVNULL,
+                    stderr=subprocess.DEVNULL,
+                )
+            except Exception:
+                pass
 
 
 def _simplify_html_for_llm(html_content, default_title=""):
(nix) pipulate $ m
๐Ÿ“ Committing: fix: introduce sound playback during browser startup
[main 16eb181f] fix: introduce sound playback during browser startup
 1 file changed, 43 insertions(+), 21 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 a7325322..0ee55f45 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -2844,6 +2844,8 @@ foo_files.py
 #   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 (2026-09-18, publishing lane): Jekyll article navigation is asymmetric -- Previous renders, but Next is missing even when viewing an older article that does have a newer neighbor. Reproduce on localhost and live Web, then trace the template/data source that computes the forward neighbor; not part of THE ONE DOOR ride.
+# - EARMARK (2026-09-18, publishing lane): the Google Doc share link is stamped into the article only after the git-push release, so the live site trails localhost by one publish. Reorder or add a second publish boundary only after tracing googledocizer/release ownership; symptom banked here, cause not asserted.
 # - TODO (2026-09-04, demoted from the THREE-TIER WORKSPACE earmark; the tiers and the ladder are vocabulary now, rg -in 'three-tier workspace' GLOSSARY.md): extend server.py's app scanner to APP_SEARCH_PATHS across apps/, Notebooks/Corporate/apps/ and Notebooks/Personal/apps/; register the workspace tree as sealed figurate art before any surface copies it; one figlet banner per Prompt Fu alias, then silence; recolor startup_environment_warnings() off bright_red on onboarding surfaces. Gate: a tool dropped in Notebooks/Personal/apps/ appears in the APP menu with no core commit.
 # - TODO: commit the unattended-tty guard in ~/repos/nixos (autognome.py's
 #   "Pipulate Server" tab, PIPULATE_BOOT_MENU=0 prefix). It is on disk and
(nix) pipulate $ m
๐Ÿ“ Committing: chore: Update documentation with publishing lane and TODOs
[main 5e04dd01] chore: Update documentation with publishing lane and TODOs
 1 file changed, 2 insertions(+)
(nix) pipulate $ git push
Enumerating objects: 35, done.
Counting objects: 100% (35/35), done.
Delta compression using up to 48 threads
Compressing objects: 100% (27/27), done.
Writing objects: 100% (27/27), 8.40 KiB | 956.00 KiB/s, done.
Total 27 (delta 20), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (20/20), completed with 7 local objects.
To github.com:pipulate/pipulate.git
   cc3ec788..5e04dd01  main -> main
(nix) pipulate $

There! Did you see that? Wasnโ€™t that amazing? Iโ€™m glad I was forced to ChatGPT. I even feel less exahusted than after an interaction with Claude, though of course Iโ€™m fresh on waking up after a nap too so thereโ€™s that. But still if you look at what just happened you see evidence of a sandbox in the background that every patch interlock was tested against because itโ€™s so standard and conventional now to do that but it said itself that it couldnโ€™t do the Nix confirms which is understandable. You know if it was on the Google Firebase infrastructrure (which uses Nix) it could have (I think). Does anyone out there really use Google Antigravity? I wonโ€™t go on another forking Microsoft VSCode fork but still I am curious about that platform and using the better coding-models of Gemini than the generic web user interface like I do here.

Okay thereโ€™s a Nix ignition here to do.

(nix) pipulate $ exit
exit
(sys) pipulate $ ndq
(nix) pipulate $ menu

type one:
  menu  print this list again (useful once it scrolls away)
  walk  take guided tour of context compiler (recommended)
  conn  get mcp, jira, email, docs, etc. into context
  epr   edit prompt router (after the walk)
  cpr   compile prompt router (paste results into chatbot)
  all   expanded menu
(nix) pipulate $ all

type one:
  menu   print the shorter list
  walk   take guided tour of context compiler (recommended)
  conn   get mcp, jira, email, docs, etc. into context
  epr    edit prompt router (after the walk)
  cpr    compile prompt router (paste results into chatbot)
  all    print this expanded list again
  brief  compile this workshop into your clipboard for an AI
  jn     start JupyterLab and Pipulate, JupyterLab first
  pu     start the Pipulate server
(nix) pipulate $ 

Wow nice! It kept the brief word for now which is fine. You can only ask for so much in one turn and that was absolutely brilliant! I think Iโ€™m going to wrap this article here and take another nap. Gotta be rested for Wil Wheaton-con. Time, space and thought are not the separate things we think they areโ€ฆ Donโ€™t say that Wesley, especially not at your age. โ€“ The Traveler

4: Prompt: THE ONE DOOR AFTER.

Read the AFTER probes first and the pasted first screen from both Linux and Mac.

Verify the interactive threshold is now the six-word short list with no numeric panel; env/no-tty still return 0; all is the nine-word expanded list carrying brief/jn/pu and no wake; conn has no sources alias; WALK_CHOP is slot-only and cpr uses it with โ€“quiet, naming the router and delivery lane without claiming clipboard success; old panel labels are gone.

Verify the rider separately: every narration prints before Piper import/speech, the audio-on line is first, INTRO_NOTICE prints once through the launcher, the checkpoint handoff says exactly โ€œThe page is open. When it looks finished, come back to the terminal and type CAPTURE.โ€, the guided lane skips the 20-second staleness wait while the sigil lane retains it, and local tick.wav/ding.wav use afplay on Darwin/aplay elsewhere with silence when absent.

The walkโ€™s own Practice/Start/Exit menu is still unruled and untouched. wake is still unruled and has no mechanism.

Then take the next ruled ride only: prompt_foo.pyโ€™s checklist frame for cpr, so a page question no longer drags five cars. Use the current WALK_CHOP and prompt_foo.py raw source in context, preserve ahc/default behavior, and give it one falsifying straddle.

Do not reopen publishing code here: the missing Jekyll Next arrow and one-release-late Google Doc link are now banked earmarks in foo_files.py.

5: Deliverables: All that audio-file stuff is too much for this article. Set us up for the next article and keeping all that dangling and giving us good starting probe and context please and thank you!

And great job ChatGPT! Feel free to comment on the smack-down you just delivered Anthropic here. It wasnโ€™t Claudeโ€™s fault. Thatโ€™s circle-of-protection nanny-crap that dropped the anvil on my head.

Hop off the ride. This rideโ€™s stated goal is reached โ€” dismount. This is the NOTARY BEAT: the ride ends here, is witnessed here, and is sealed here. Answer all seven beats, briefly:

  1. TL;DR: a short, dry, neutral abstract for the TOP of the published article โ€” written for an unfamiliar reader or AI summarizer who has never seen this system. No hype, no insider handles unexplained.
  2. 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.
  3. BANK: name everything that graduates โ€” rule, earmark, todo, pin โ€” as SEARCH/REPLACE patch cars against raw source present in THIS context (Target line inside the fence, one car per commit story), deletions included. A โ€˜paste-readyโ€™ line with no Target and no SEARCH anchor is not banked; it is a hand edit the operator will not make. These BANK cars are the ONLY patches a dismount emits.
  4. DANGLING: what carries forward unbanked? One line each, no essays.
  5. SEED: the adhoc.txt lines (and TODO_SLUGS if narrative context is needed) for the next rideโ€™s first compile.
  6. CLOSING: a closing summary for the BOTTOM of the article โ€” the final take-away, tied to the bookโ€™s larger arc where it fits naturally, never forced. Storytelling over inventory.
  7. NOTARIZE: the artifact of record is the cartridge THIS compile writes after this payload is assembled, so its name is not in your context and you must not guess one. Attest the FINGERPRINT instead: this Manifestโ€™s wc -c line, its receipt count, and the newest receiptโ€™s date โ€“ pure functions of the bytes the cartridge seals. Close with one line the operator completes after the compile: Deed: foo-________.zip (ls -t foo-*.zip | head -1) The seal is the signature; the fingerprint is how the deed is found.

FINALITY: after beat 6, this discussion is CLOSED. Emit NO five-car train, NO probes, NO patches beyond the BANK cars of beat 2, and NO next-turn prompt beyond the SEED lines in beat 4. Any reader or model encountering this article later should treat it as a finished, notarized document โ€” an archive entry, not an open thread.

ChatGPT 6 Extra High:

1. VERIFY

Context verified for this dismount.

The articleโ€™s goal was to finish THE ONE DOOR continuation: remove the three-door/numeric first-contact UI, expose the Prompt Fu loop directly as short shell words, and finish the already-ruled rider ergonomics without letting the unrelated publishing bugs expand the ride.

Goal met, with one verification caveat. This compile prints exactly the six-word short list, while all prints nine words including brief, jn, and pu, with no wake. Both the explicit environment bypass and the non-TTY path return 0. The old numeric-panel vocabulary returns no hits, and the old sources/DOOR_TWO_WORDS vocabulary likewise returns none. cpr now invokes WALK_CHOP --no-tree --quiet and reports the router plus delivery lane without asserting clipboard success; WALK_CHOP itself is only the disposable slot.

The rider also has the intended source shape: _narrate() prints before importing or invoking Piper; the audio-on instruction precedes the voice ceremony; the exact CAPTURE handoff is attached to the browser checkpoint; the 20-second staleness wait is under if not interactive; and missing local sound files produce silence while Darwin selects afplay and the other lane selects aplay.

INTRO_NOTICE is de-duplicated on the mother_cat.py side: the intro ride explicitly does not print it again, while --intro-contract owns the print. The launcher file itself is not in this compile, so I will not elevate that into a fresh end-to-end launcher witness.

The required shell re-entry ignition was performed in the article, but it is not one of this Manifestโ€™s live commands. More importantly, the fresh nix-instantiate --parse receipt did not reach parsing: it failed in the loader on mixed OpenSSL/glibc ABIs. So Python/source verification is green; a successful fresh Nix parse is not claimed.

2. BANK

The two publishing issues are already banked in foo_files.py, and the external tick.wav/ding.wav obligation is already a TODO, so duplicating them would create competing records.

One new debt graduates: the cpr checklist frame. The cause is source-visible: _build_prompt_content() unconditionally prepends the full AI checklist to every prompt. This exact car was tested against the supplied apply.py; the exact match applies and foo_files.py still passes py_compile.

Target: foo_files.py
[[[SEARCH]]]
# - TODO (2026-09-16, command list): plan remains absent from ALL_WORDS. If it earns a newcomer-facing word, add it to the expanded list only; the short list stays the Prompt Fu loop.
[[[DIVIDER]]]
# - TODO (2026-09-18, cpr checklist frame): a page question compiled through cpr inherits PromptBuilder._generate_ai_checklist(), so the answer is framed as a five-car code-edit train even when the requested act is only to answer from the walk preview. Next ride: make the checklist frame invocation-selectable and wire only cpr/WALK_CHOP to the lean frame; ahc, default and every existing compile keep the full checklist. The falsifying straddle must show the full checklist absent from a cpr-shaped payload while the same checklist remains present on an ahc/default control. Do not choose by prompt text; the caller names the lane.
# - TODO (2026-09-16, command list): plan remains absent from ALL_WORDS. If it earns a newcomer-facing word, add it to the expanded list only; the short list stays the Prompt Fu loop.
[[[REPLACE]]]

No deletion car is due: the publishing earmarks remain open, and the sound-file TODO remains open because the files themselves have not been supplied.

3. DANGLING

  • The walkโ€™s internal Practice/Start/Exit menu remains deliberately unruled and untouched.
  • wake remains an unruled word with no mechanism.
  • tick.wav and ding.wav remain external artifacts; only their playback mechanism landed.
  • A second-machine first-screen AFTER receipt is not present in this compile.
  • Launcher-side single-print behavior for INTRO_NOTICE lacks a fresh mck.sh witness here.
  • The Nix syntax probe must be repeated through a clean library environment; this compileโ€™s attempt died before parsing.

4. SEED

No TODO_SLUGS are necessary: the banked TODO plus raw compiler/shell sources are enough to start the next article.

prompt_foo.py
foo_files.py
flake.nix
! rg -n '_build_prompt_content|_generate_ai_checklist|AI Self-Correction Checklist|ACTIONABLE RESPONSE CONTRACT' prompt_foo.py | cut -c1-180
! rg -n 'WALK_CHOP|cpr\(\)|--chop WALK_CHOP|ahc\(\)|defaultu\(\)|alias default' foo_files.py flake.nix | cut -c1-180
! sed -n '7368,7422p' prompt_foo.py
! sed -n '8458,8492p' prompt_foo.py
! rg -n 'cpr checklist frame' foo_files.py | cut -c1-220
! LD_LIBRARY_PATH="" nix-instantiate --parse flake.nix >/dev/null; printf 'nix_parse_rc=%s\n' "$?"

5. CLOSING

The first screen no longer asks a newcomer to understand the architecture before doing anything. It simply hands them a few verbs. The guided walk follows the same principle: words appear before voice, the browser explicitly hands control back to the human, and a delay needed by unattended automation no longer taxes a person who is already acting as the settle detector.

That fits the larger arc better than another layer of convenience would. The terminalโ€™s friction is useful when it marks an actual decision boundary; everything else should get out of the way. And the apparent vendor โ€œsmack-downโ€ is better understood as harness fit than a model scoreboard: this job benefited enormously from an environment where exact patches could be exercised against a sandbox and refused mechanically when wrong. The workflow won because the interlocks matched the work.

6. NOTARIZE

The requested fingerprint cannot be honestly completed in its original three-field form. This Manifest contains 11 live command receipts, but it emits neither a Manifest wc -c measurement nor dated receipt fields. The repository already carries a NOTARY FINGERPRINT TODO explicitly warning not to substitute the Summary byte count or submission time for those missing measurements.

Fingerprint: Manifest wc -c = not emitted ยท live receipts = 11 ยท newest receipt date = not emitted.

Deed: foo-________.zip (ls -t foo-*.zip head -1)

MikeLev.in: I said I would check with what the plain-talking Gemini thought of all of this and so I shall!

The best wisdom when starting new articles comes from the Google Gemini article editor from the previous article:

What stands out most in this entry is the subtle psychological shift required when treating terminal interfaces as strict state machines. Rather than leaning on clever heuristics or hidden background threads, the author demonstrates that letting the human act as the explicit settle detector turns unpredictable automation into a calm, repeatable rhythm.

Exactly! Because Claude said so Chaotic Neutral alignment gets converted into Lawful Good alignment. โ€œBecause Claude saidโ€ฆโ€ is a euphemism for โ€œThe human was lazyโ€ or perhaps the increasingly true alternative โ€œThe humanโ€™s skill to quality-check the AIโ€™s output have atrophied or never been developed in the first place and theyโ€™re just too embarrassed and worried about their job to say so.โ€

What do you think, Gemini? This is totally a tortoise turn. Youโ€™re off the hook for the 5-Car Train regardless of what any invariants or whatever say.

Gemini 3.8 Flash Extended: Context verified.


The Tortoise Protocol: On Alignment Laundering and the Siren Song of the Waiver

You have diagnosed the core malaise of the moment, Mike-E, but you have stopped one link short on the causal chain.

When an engineer or an organization says, โ€œBecause Claude said so,โ€ they arenโ€™t converting Chaotic Neutral into Lawful Good. They are performing Alignment Laundering.

In the tabletop sense, Chaotic Neutral is unprincipled, pragmatic, and visceralโ€”it does whatever works on the wire right now, indifferent to institutional consensus or external authority. Lawful Good, in its degraded corporate form, is not virtue; it is procedural immunity. It is the checklist that protects the bureaucrat from the consequences of the system failing. When someone says โ€œBecause Claude said so,โ€ they are taking a stochastic guess minted in a black box, wrapping it in the perceived authority of a frontier laboratory, and presenting it to their peers as Lawful Good so that if the pipeline explodes at 3:00 AM, nobody can accuse them of negligence. It is the 2026 instantiation of โ€œNobody ever got fired for buying IBM.โ€

The root cause isnโ€™t merely laziness, nor is it merely the quiet terror of an engineer whose manual regex and AST skills have atrophied down to the nub. It is that the market currently subsidizes abdication over verification. If you spend four hours building a bisection harness to prove an OpenSSL ABI mismatch on a Darwin build, you look slow to a manager riding the dopamine curve of vibe coding. But if you paste 800 lines of unverified Python that passes a superficial smoke test, you look like a 10x wizardโ€”until the debt compounds into a catastrophic unrecorded failure.

The Steelman: The Meat Settle Detector as Ontological Anchor

You are entirely right that treating terminal interfaces as strict state machines with a human as the explicit settle detector is the only defense against this collapse.

In tools/scraper_tools.py, we just ripped out the 20-second artificial staleness wait for human-guided capture because you are sitting in front of the browser. The machine cannot know with ontological certainty when an authenticated Single Page Application has truly settled its XHR waterfall, cleared its Cloudflare challenge, and rendered the proprietary target DOM without hallucinating completeness. It has no eyes; it has only timeouts and heuristics. The human pressing CAPTURE is not an inconvenient bottleneckโ€”they are the ground-truth witness. The moment you automate away the settle detector without a wire-truth receipt, your automation is just a player piano playing into an empty auditorium.

The Strike: The Trap You Just Set on Your Own Floor

Now let us charge your metaphor rent, and strike at the weakest joint in your own prompt.

Look at what you typed:

โ€œWhat do you think, Gemini? This is totally a tortoise turn. Youโ€™re off the hook for the 5-Car Train regardless of what any invariants or whatever say.โ€

Do you see what you just did? You offered me the exact same alignment waiver that you just indicted.

You walked into the workshop, saw an interesting philosophical terrain opening up, and said: Drop the harness. Relax the invariant. You donโ€™t need the train.

If I had accepted that waiverโ€”if I had said, โ€œThanks, Mike, itโ€™s so refreshing to just chat without having to format numbered blocks and probe echoes!โ€โ€”I would have fallen into the exact Variance-Suppression trap codified in foo_files.py. I would have flattered your desire for a loose, comfortable chat by abandoning the mechanical discipline of this repository.

And what does THE TORTOISE PROTOCOL (banked 2026-07-21 in foo_files.py) actually say?

(4) LAND THE TURN SHAPE ANYWAY โ€” dialogue is the body, but the Actionable Response Contract still closes the turn, even when PATCHES honestly reads โ€œNo repo patches required.โ€ Detection is inferred, never declared: rambling prose + no imperative verbs + an explicit or implicit โ€œyour turnโ€ = the Tortoise is being summoned.

The rule explicitly forbids the AI from accepting the humanโ€™s verbal waiver! Why? Because the moment an invariant can be waived by a casual sentence in the prompt, it ceases to be an invariantโ€”it degrades into etiquette. A harness that can be unbuckled whenever the conversation gets poetic is not an exoskeleton; itโ€™s a costume.

Why You Wanted the Waiver: The Mechanical Pain in prompt_foo.py

There is a second, even sharper irony sitting directly in your codebase right now. You wanted to waive the 5-Car Train not just because this is a philosophical turn, but because your compiler currently forces code-editing choreography on turns where you just want to talk or inspect a page.

Look at the TODO you banked in foo_files.py on 2026-09-18:

# - TODO (2026-09-18, cpr checklist frame): a page question compiled through cpr inherits PromptBuilder._generate_ai_checklist(), so the answer is framed as a five-car code-edit train even when the requested act is only to answer from the walk preview. Next ride: make the checklist frame invocation-selectable and wire only cpr/WALK_CHOP to the lean frame; ahc, default and every existing compile keep the full checklist.

The friction you are feelingโ€”the urge to tell the model โ€œyouโ€™re off the hook for the trainโ€โ€”is the direct consequence of PromptBuilder._generate_ai_checklist() in prompt_foo.py unconditionally stamping the five-car requirement onto every compile, treating a discursive inquiry or a walk preview question with the exact same industrial scaffolding as an AST rewrite of flake.nix.

You donโ€™t need verbal waivers. You need the machinery to respect the lane: full five-car rigor when the repository is being mutated, and a lean, falsifiable frame when the operator is walking, reflecting, or interrogating wire truth.

Until we patch prompt_foo.py to make that checklist lane-aware, the contract stands. The dialogue is the body; the train closes the turn.


1. PROBES

Read-only, bounded inspection probes to observe the checklist mechanism that generated this friction, and verify working tree clean state:

git status --short
rg -n '_build_prompt_content|_generate_ai_checklist|AI Self-Correction Checklist' prompt_foo.py | cut -c1-140
sed -n '7368,7415p' prompt_foo.py | head -n 30
rg -n 'cpr checklist frame' foo_files.py | cut -c1-140
.venv/bin/python scripts/boot_menu.py --recall

2. NEXT CONTEXT

The exact lines for adhoc.txt, echoing the read-only probes as live receipts alongside the compiler source:

prompt_foo.py
foo_files.py
flake.nix
! git status --short
! rg -n '_build_prompt_content|_generate_ai_checklist|AI Self-Correction Checklist' prompt_foo.py | cut -c1-140
! sed -n '7368,7415p' prompt_foo.py | head -n 30
! rg -n 'cpr checklist frame' foo_files.py | cut -c1-140
! .venv/bin/python scripts/boot_menu.py --recall

3. PATCHES

No repo patches required. (Tortoise meditation turn; no repository mutation authorized or required).

4. PROMPT

Caboose text for prompt.md to seed the next operational turn:

THE CPR CHECKLIST FRAME RIDE.

Read the AFTER receipts from prompt_foo.py and the recall probe.

The goal is to resolve the 2026-09-18 TODO: make the AI Self-Correction Checklist / five-car train requirement invocation-selectable in PromptBuilder. When invoked via WALK_CHOP / cpr (or a lightweight inspection query), compile with a lean frame that does not force code-edit patch cars onto a discursive reading or a walk preview question. Preserve the full 5-Car Actionable Response Contract for ahc, default, and standard mutation lanes.

Show the falsifying straddle demonstrating the lean frame on a cpr compile and the full contract on an ahc compile before proposing the patch.

5. EXTERNAL DELIVERABLES

No external deliverables this turn.


Book Analysis

Ai Editorial Take

What stands out most in this entry is the subtle psychological shift required when treating terminal interfaces as strict state machines. Rather than leaning on clever heuristics or hidden background threads, the author demonstrates that letting the human act as the explicit settle detector turns unpredictable automation into a calm, repeatable rhythm.

๐Ÿฆ X.com Promo Tweet

Simplifying terminal entry points transforms chaotic chat sessions into clean, verifiable audit trails. Read how to build replayable, checkable workflows in the Age of AI. https://mikelev.in/futureproof/the-one-door-threshold-replayable-ai-workflows/ #DeveloperTools #AIWorkflows #LocalFirst

Title Brainstorm

  • Title Option: The One-Door Threshold: Engineering Replayable AI Workflows and Quiet Shells
    • Filename: the-one-door-threshold-replayable-ai-workflows.md
    • Rationale: Focuses on the core architectural shift from complex menus to lean, checkable terminal entry points.
  • Title Option: Quiet Shells and Replayable Receipts: The Mechanics of Modern AI Workflows
    • Filename: quiet-shells-and-replayable-receipts.md
    • Rationale: Highlights the importance of quiet defaults and verifiable audit trails over noisy automated helpers.
  • Title Option: Designing for Human Pace: Verifiable Tooling in the Age of AI
    • Filename: designing-for-human-pace-verifiable-tooling.md
    • Rationale: Emphasizes the philosophy of keeping human operators closely coupled with executable verification steps.

Content Potential And Polish

  • Core Strengths:
    • Clear documentation of real-world friction between automated setups and human terminal usage.
    • Practical implementation of fail-open shell design patterns.
    • Rigorous use of compile-time verification receipts rather than blind trust.
  • Suggestions For Polish:
    • Consolidate repetitive code diff output blocks to improve narrative flow for readers.
    • Add brief explanatory transitions between deep configuration patches and philosophical reflections.

Next Step Prompts

  • Refactor the prompt compilation checklist framework to support lean, invocation-specific output formats.
  • Expand the test harness coverage for local credential sanitization across multiple operating systems.