The Letting-Go Moment: Building Vim Training Wheels for the Age of AI

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

Setting the Stage: Context for the Curious Book Reader

When software developers talk about autonomous agentic loops, they often celebrate removing the human from the loop entirelyโ€”pulling the ripcord and letting an LLM generate code overnight without oversight. This entry challenges that doctrine from the perspective of craftsmanship and user empathy. While refining the onboarding tour for Pipulate on macOS and Linux, the author realizes that handing a newcomer an unconfigured NeoVim prompt router without explanation is the software equivalent of shoving someone down a steep hill without training wheels. The solution is not to hide the terminal behind opaque abstractions, but to design gentle, self-shedding scaffolding: temporary explanatory headers, clear exit keys, and an explicit introduction file for the receiving model. True mastery in the Age of AI does not come from letting critical cognitive skills atrophy behind automated black boxes; it comes from building checkable, inspectable bridges that empower humans to steer the machine.

TL;DR: This article records one working session on Pipulate, a small context compiler that runs on a personโ€™s own computer and assembles files, command output, and a question into a single text payload for an AI chat window. The session fixed a rough edge in the toolโ€™s guided introduction: after the walk, a newcomer is told to open a file in the vim editor with no instruction on how to move or leave. The file the walk writes now opens with a short lesson (two keys to move, one command to quit, the old joke about being unable to exit vi), a new introduction.md tells the receiving chatbot what it is reading and who compiled it, the spoken closing names the next step, and a volume hint prints after the first spoken sentence. The lesson is written only when the file did not exist before the walk; later walks write a three-line header. Every change was measured by the same command run before and after the edit, and the result was witnessed on a fresh macOS install. The article also argues, from the sessionโ€™s own record, that this kind of design is discovered by iteration rather than specified in advance.


Technical Journal Entry Begins

๐Ÿ”— Verified Pipulate Commits:

MikeLev.in: Right, so the Gemini Editorial take from the last article says it again:

What stands out here is that prompt context is treated not as a static configuration file, but as biological tissue undergoing metabolic turnover (catabolism and remodeling). Instead of treating context expansion as purely an optimization problem for the compiler, the author demonstrates that living systems must actively excrete and store spent detail to prevent functional clogging. The limiting reagent in modern AI development has quietly inverted: the models have ample memory, but the human operatorโ€™s attentional capacity to audit sequential transformations is the true boundary.

The human is the bottleneck. But if the human is the one calling the shots and the machine can work much faster and get our ahead of you, then how can the human continue to call the shots? Ohhh, agentic loops and putting all the pedantic detail into โ€œwhat finished looks likeโ€ and then let the self-prompting ouroboros loop eat its tail until its done?

Is that it?

Everybodyโ€™s doing it. Everybody was Kung Fu fightingโ€ฆ Hmmm. Everybody was Agentic Looping!

Be the human brave enough and with enough capabilities enough and determined enough to not only not let your skills atrophy, but to force the exact skills that are going to be atrophying in everyone else to improve!

Have an inverse relationship to the rest of the world, as theyโ€™re being conditioned to rely on the specific frameworks being provided by AI companies rely instead on my own framework. Make my own framework the cure to the โ€œBecause Claude said so blues!โ€

Resisting Skill Atrophy in the Rush to Automate

That last forgetting with style started at 1:30 PM and itโ€™s now coming up on 3:30 PM so that was 2 hours of that as a warm-up for the work I really need to do, but it became fatiguing because I wanted to see how far I could take it because I had to in order to make a dent of only 3% file-size reduction. That doesnโ€™t sound like much but itโ€™s a lather, rinse, repeat process I can do a little every day so thatโ€™s fine.

I just have to remember to forget is all.

Now letโ€™s get to the hard work thatโ€™s going to take some thinking calories. Do you see the difference? Using the forgetting kata is just a kata which means it requires very little thinking. In Karate Kid terms itโ€™s the wax-on wax-off practice. You donโ€™t have to think much. You just do the moves and you myelinate the skill. It becomes like riding a bike. Thatโ€™s why I took a moment there to improve the init.lua keyboard shortcut for the experiment template, if that makes any sense. Iโ€™m not even going to include the previous article for it to make sense for the next model because it would be wasteful of tokens but from looking at the 40K-foot of the system the Model is still going to know what Iโ€™m talking about.

Anyway, the latest install from macOS looks like this:

(nix:nix-shell-env) (nix) npvg $ exit
exit

Saving session...
...saving history...truncating history files...
...completed.
Deleting expired sessions...none found.
michaellevin@MichaelMacBook-Pro ~ % rm -rf npvg
michaellevin@MichaelMacBook-Pro ~ % rm -rf ~/.config/pipulate 
michaellevin@MichaelMacBook-Pro ~ % curl -fsSL https://npvg.org | bash
Npvg -> ~/npvg   (to remove it later: rm -rf ~/npvg)
Deploy key saved to .ssh/rot (public, pull-only: it lets this folder fetch updates without a GitHub account).
To come back later:  cd ~/npvg && nix develop
Hydrating the Nix environment (the first time can take a few minutes)...
Restored session: Sat Sep 19 22:33:12 EDT 2026
โœ… Transformed into a git repository (pre-transform files backed up to /tmp/nix-shell.iw7xJU/tmp.1sreprsnsQ).
Already up to date.
Found ~/.ssh/id_rsa -- switching the git remote to SSH so auto-updates use it.
(Determinate Nix 3.19.1) 2.34.6 ยท Python 3.12.13 ยท v2.58 ยท ~/npvg
๐Ÿ”ง Fresh install detected โ€” packages downloading...
โœ… 291 packages ready.
๐Ÿ““ 12 starter file(s) copied into Notebooks/ -- yours to edit.

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:nix-shell-env) (nix) npvg $ 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:nix-shell-env) (nix) npvg $ 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:nix-shell-env) (nix) npvg $ walk
Trail resolved: assets/trails/public_walk.json

This walk opens three public pages. Nothing to sign in to.
Return here and type CAPTURE when prompted at each page.
After all three captures and successful checks, it saves a private summary
and replaces your clipboard. Over SSH it uses a bridge file.
Nothing is sent to a chatbot. Review the summary before sharing it.

Choose a walk:
  1  Practice - read the steps; no pages open.
  2  Start the walk - open the pages.
  q  Exit (Enter also exits).
Choice: 2
Riding trail 'public_walk' -- 3 stop(s).

Make sure your audio is turned on.

The steps can be read aloud.

The voice is Piper, a small text-to-speech program that runs on this
computer; it was made for a Raspberry Pi. It reads sentences a person
typed in advance. No AI writes them, and nothing you say or type is
sent anywhere. Answering yes downloads the voice once, about 60 MB.

Read the steps aloud? [y/N] y

This walk opens three pages. You do not need to click anything. At each page, return here and wait for the CAPTURE prompt. Type CAPTURE and press Enter.
Downloading the voice, about 60 MB, once...
Voice ready.

Summary file: data/decant-preview.md (private; replaced on save).
Checks can miss private details. A blocked check leaves the older file alone.

--- Stop 1/3: the_word ---

Opening page one in the browser. Leave the browser open; it closes on its own after capture. When the page is ready, return here and type CAPTURE.
Opening the browser; waiting for the page...

The page is open. When it looks finished, come back to the terminal and type CAPTURE.
Any other response aborts without capturing artifacts.
CAPTURE> CAPTURE
  LOCAL ARCHIVE  /Users/michaellevin/npvg/data/captures/walk-7_r2ybpy/captures.md  (directory 0700, file 0600)
  Captured. final_url=https://npvg.org/walk/1/ artifacts=12
  ADVANCE -> next stop.

--- Stop 2/3: the_receipt ---

Opening page two in the browser. Leave the browser open; it closes on its own after capture. When the page is ready, return here and type CAPTURE.
Opening the browser; waiting for the page...

The page is open. When it looks finished, come back to the terminal and type CAPTURE.
Any other response aborts without capturing artifacts.
CAPTURE> CAPTURE
  Captured. final_url=https://npvg.org/walk/2/ artifacts=12
  ADVANCE -> next stop.

--- Stop 3/3: the_two_pages ---

Opening the last page in the browser. Leave the browser open; it closes on its own after capture. When the page is ready, return here, type CAPTURE, and read the result.
Opening the browser; waiting for the page...

The page is open. When it looks finished, come back to the terminal and type CAPTURE.
Any other response aborts without capturing artifacts.
CAPTURE> CAPTURE
  Captured. final_url=https://npvg.org/walk/3/ artifacts=12
  ARCHIVE STATUS  complete

Local archive file line for context.md or adhoc.txt:
/Users/michaellevin/npvg/data/captures/walk-7_r2ybpy/captures.md
Review locally before compiling; raw bytes are not a safe disclosure.
  WALK ROUTER  /Users/michaellevin/npvg/adhocwalk.txt  (0600; names the UNSANITIZED archive until a preview is released)

Ride complete. Every stop produced a capture receipt.

Checking the summary before saving it and trying the clipboard.
   Preview checks: substitutions=0 denylist=0 secrets=0
   LOCAL PREVIEW /Users/michaellevin/npvg/data/decant-preview.md (0600; sha256=c18827794aafb3dc8c65619d99820d7a878db9a728ebb7eae23758882ee53a10)
   WALK ROUTER  /Users/michaellevin/npvg/adhocwalk.txt  (0600; the preview rides, the archive is commented beneath it)
Markdown output copied to clipboard
   Read the save and copy messages above; either step can fail.
   Review the summary before sharing it. You choose what to send.

The three-page walk is finished. Read the save and copy results in your terminal. Review any summary before sharing it. Goodbye.
--------------------------------------------------------------
   CAPTURE RUN FINISHED
--------------------------------------------------------------
 Read the save and copy messages above. Either step can fail.
 Review the summary before sharing it.
 Nothing was sent to a chatbot.
--------------------------------------------------------------

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:nix-shell-env) (nix) npvg $ epr
(nix:nix-shell-env) (nix) npvg $ epr
(nix:nix-shell-env) (nix) npvg $ cpr
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ๐Ÿฐ 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           |__>-\_>_>  \____/                     ๐Ÿฅ•๐Ÿฅ•๐Ÿฅ•                                       โ”‚
โ”‚                                                                                                                                โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
๐Ÿ“Š Stats block refreshed: 0 articles at Mac Shadow - MikeLev.in.
๐Ÿ—บ๏ธ  Codex Mapping Coverage: 74.7% (204/273 tracked files) (was 204/273: +0 claimed, +0 tracked).
ROUTER LOADED: /Users/michaellevin/npvg/adhocwalk.txt (1 active line(s))

โœ… Topological Integrity Verified: 2 candidate reference(s) scanned, all exist.
Prompt frame: lean (a reading frame; no patch train rides ahead of the prompt)
๐Ÿงผ Identity scrub: substitutions=MISSING (/Users/michaellevin/.config/pipulate/pii_substitutions.txt) | denylist=MISSING (/Users/michaellevin/.config/pipulate/commit_denylist.txt)
๐Ÿ” Secrets tripwire: ARMED โ€” 0 hit(s) in payload.
๐Ÿ“ฆ Canonical context cartridge written to /Users/michaellevin/npvg/foo.zip (sha256=32bd02e424baโ€ฆ, members=3)
๐Ÿ—‚๏ธ  Rotated cartridge snapshot: foo-32bd02e4-01.zip (keeping newest 1 of 20)
Markdown output copied to clipboard
Read: /Users/michaellevin/npvg/adhocwalk.txt
Delivery lane: system clipboard
(nix:nix-shell-env) (nix) npvg $ 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:nix-shell-env) (nix) npvg $

Not bad. But the bad part is the contents of:

(nix:nix-shell-env) (nix) npvg $ epr

  4 # Written by the last completed walk. Edit freely: an edited router is never replaced.
  3 # One line per thing the AI should see: a file path, or a command after `! `.
  2 # The checked preview rides. The whole UNSANITIZED archive is the commented line beneath it.
  1 /Users/michaellevin/npvg/data/decant-preview.md
5   # /Users/michaellevin/npvg/data/captures/walk-7_r2ybpy/captures.md
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
adhocwalk.txt                                                                                                   5,1            All
-- [ ] - [ ] - VISUAL LINE --                                                                                                      5  

Thatโ€™s the loading of NeoVim for the first time. Do you see something wrong with this as the first experience? Hmmm. Iโ€™m going to include in enough context so that you can see their first introduction to the less program in peeking at the installer. Hmmm. Letโ€™s see.

Okay, thereโ€™s a lesson here about setting context. Some people will point to a repo root and say this is my context. Some people will select the files they think should be in context for the prompt. The frameworks themselves Iโ€™m led to believe are getting better at guessing what the context should be, but I stopped using Cursor IDE at about when it went from beta to version 1. I spent what feels like over a year on a forking VS Code fork and it was as bad as I imagined. When I set context like thisโ€ฆ

release.py                                 #  <-- The deploy process
assets/installer/install.sh                #  <-- Pipulate.com installer real home in github/pipulate repo
~/repos/Pipulate.com/_layouts/default.html
~/repos/Pipulate.com/install.md            #  <-- Gets copied into place here by pipulate/release.py
nixops.sh                                   # <-- You've heard of GitOPs? Well, this is NixOPs. 
remotes/honeybot/hooks/post-receive         # <-- Ever hear of GitHub Pages? Or github.io? This is that.
remotes/honeybot/nixos/configuration.nix    # <-- It's as if Pipulate had kids. Spy kids.

Well Fable 5.1, you explain the difference between leaving it to the luck of the draw and whatever token-burning methods the AI company-provided frameworks do to set good initial versus this. Itโ€™s not just about file-selection.

The LLM knows the hardware infrastructure down to the versions. You might even say down to the metal for all intents and purposes. Iโ€™ve just dumped enough for the model to technically recreate my entire system with their sandbox. If its own sandbox infrastructure was Nix-based the way Firebase Studioโ€™s declarative and reproducible workspaces are entirely Nix-managed. Chances are itโ€™s some Docker, Kubernetes combo here so it canโ€™t really reproduce my system using my own Infrastructure as Code (IaC) files that I provided, but there is still advantages in terms of clarity, purging ambiguity and overall elimination of opportunities to hallucinate.

I make such an overwhelming case of the internal consistency and efficacy of what Iโ€™ve built that I steamroll it against poising my repo with the so-called Fast API that shall not be named which much of my patterns look like but arenโ€™t. I drop a world-shaping reality on the LLMโ€™s head like an ACME Anvil on Wile E. Coyote. Iโ€™ve made a Gravity well so heavy you can see the lensing. Every bit of it is counter-intuitive: Nix over Docker, HTMX over React.

I state this in a poem that the AI reads every time it compiles context:

Here is my hardware
Here is my state
Put on your sandbox
And please recreate.

Oh so anyway look at how we tell the user about j, k and q in the install experience when they doโ€ฆ hmmm. I can point to to the npvg.org homepage at this point to show it but itโ€™s better to use my 2nd brain tools to figure out what file exactly to put in context.

(nix) pipulate $ rg 'curl -fsSL https://npvg.org \| bash'
foo_files.py
144:# 2026-09-14 dismount THE DOOR NAMES THE FOLDER (four compiles ending at deed 1391; commits d84b4b35, a173de69, 543fb663, 45717d36; PyPI 2.56): one installer, two addresses, two default folders. install.sh carries __INSTALL_DEFAULT_NAME__ and compares it against the same word spelled in two halves; npvg.org's nginx (http_sub_module compiled into the running 1.28.0, count 1 on both taps) stamps it to npvg with sub_filter_types text/x-shellscript and sub_filter_once off; pipulate.com serves the file untouched and the fallback stays pipulate. The label split rode the same file: only an explicit argument writes whitelabel.txt, so a default install lets the flake name the app. Both trucks fired (nixops.sh, then nixos-rebuild switch building 22 derivations and restarting nginx.service; the publish-only release lane, then a full 2.56 release), three digests agreed at 064cad99, and the round-trip gate held: the served npvg body with its one token reversed hashes to the repo file. From inside the house on the split-DNS LAN, curl -fsSL https://npvg.org | bash landed in ~/npvg, printed no app-identity line, read v2.56 and 290 packages, and the flake wrote whitelabel.txt reading Pipulate (the capital names the writer); rm -rf ~/npvg read present -> absent and the main workshop came back as App name: Pipulate, data/pipulate_dev.db. A caboose from another ride was pasted once and ruled around from the Manifest. UNWITNESSED: the cookie-decode and Nix-not-found branches, WSL, the figlet under a default npvg install (door 2 chosen). This block reads 26 lines against its cap of 20; the next forget ride fades six.

remotes/honeybot/www/npvg.org/index.html
18:<pre>curl -fsSL https://npvg.org | bash</pre>
(nix) pipulate $ 

Right, so we add remotes/honeybot/www/npvg.org/index.html to the aforementioned context. See how all of this is coming together?

Now hereโ€™s part of the rub and it puts the forgetting with style article in context. I have a handful of files I frequently โ€œedit inโ€ to establish 40K-foot view in the first turn of a discussion. And Iโ€™ve been recycling the same discussion over lately instead of starting a new discussion for each exchange of the discussion which is my norm and is helpful for model switching. But when I lay down that enormous approximatelyโ€ฆ well, let me show you exactly:

The Two-Lane Reality: Testing Across Linux and Mac

(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           |__>-\_>_>  \____/                     ๐Ÿฅ•๐Ÿฅ•๐Ÿฅ•                                                                                          โ”‚
โ”‚                                                                                                                                                                                   โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
๐Ÿ“Š Stats block refreshed: 1,490 articles at MikeLev.in (Public).
๐Ÿ—บ๏ธ  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 (11 active line(s))

โœ… Topological Integrity Verified: 47 candidate reference(s) scanned, all exist.
   -> Executing: python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs ... [0.8716s]
   -> Ruff exit 0 (clean).
                                  ๐Ÿ“ฆ Payload Ledger (biggest first)                                  
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“
โ”ƒ File / Source                                                     โ”ƒ  Tokens โ”ƒ     Bytes โ”ƒ % Bytes โ”ƒ
โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ
โ”‚ foo_files.py                                                      โ”‚  82,785 โ”‚   330,816 โ”‚   28.2% โ”‚
โ”‚ prompt_foo.py                                                     โ”‚  48,213 โ”‚   212,314 โ”‚   18.1% โ”‚
โ”‚ ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs โ”‚  63,657 โ”‚   165,794 โ”‚   14.2% โ”‚
โ”‚ flake.nix                                                         โ”‚  33,460 โ”‚   138,803 โ”‚   11.9% โ”‚
โ”‚ GLOSSARY.md                                                       โ”‚  34,374 โ”‚   138,080 โ”‚   11.8% โ”‚
โ”‚ /home/mike/repos/nixos/autognome.py                               โ”‚  10,581 โ”‚    47,651 โ”‚    4.1% โ”‚
โ”‚ init.lua                                                          โ”‚  10,416 โ”‚    39,888 โ”‚    3.4% โ”‚
โ”‚ apply.py                                                          โ”‚   8,584 โ”‚    36,636 โ”‚    3.1% โ”‚
โ”‚ PROMPT (checklist + prompt.md)                                    โ”‚   7,158 โ”‚    30,676 โ”‚    2.6% โ”‚
โ”‚ assets/installer/install.sh                                       โ”‚   4,257 โ”‚    16,852 โ”‚    1.4% โ”‚
โ”‚ pyproject.toml                                                    โ”‚   1,129 โ”‚     4,104 โ”‚    0.4% โ”‚
โ”‚ .gitignore                                                        โ”‚     970 โ”‚     3,598 โ”‚    0.3% โ”‚
โ”‚ __init__.py                                                       โ”‚     699 โ”‚     2,888 โ”‚    0.2% โ”‚
โ”‚ requirements.in                                                   โ”‚     675 โ”‚     2,340 โ”‚    0.2% โ”‚
โ”‚ AUTO: Recent Git Diff Telemetry                                   โ”‚     223 โ”‚       758 โ”‚    0.1% โ”‚
โ”‚ .gitattributes                                                    โ”‚      33 โ”‚        76 โ”‚    0.0% โ”‚
โ”‚ AUTO: Static Analysis Diagnostics                                 โ”‚      11 โ”‚        39 โ”‚    0.0% โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ TOTAL                                                             โ”‚ 307,225 โ”‚ 1,171,313 โ”‚  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 (223 tokens | 758 bytes)

--- Prompt Summary ---
Summed Tokens:    309,275 (from section parts)
Verified Tokens: 311,085 (from final output)
  (Difference: +1,810)
Total Words:      142,940 (content only)
Total Chars:      1,179,549
Total Bytes:      1,183,950 (UTF-8)

--- Size Perspective ---
๐Ÿ“š Equivalent in length to a **Epic Fantasy Novel** (Note: With a token/word ratio of 2.18, 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=9e199e62244eโ€ฆ, members=3)
๐Ÿ—‚๏ธ  Rotated cartridge snapshot: foo-9e199e62-1544.zip (keeping newest 20 of 20)
Markdown output copied to clipboard
(nix) pipulate $

Itโ€™s over a megabyte which is too big for Gemini as plain text. Thereโ€™s many little nuances here. A compiled prompt like that can be submitted either as plain text to the chatbots or as a text-file attachment and itโ€™s not always your easy choice. It can be coerced to be one way or the other and thatโ€™s part of the nuance. The details change per model. Sometimes you have to do a Ctrl+Shift+V in order for it to paste as plain text. Thatโ€™s the norm. Most of the Web UIs for ChatBots โ€œupgradeโ€ your large text paste into a text-file attachment for multi-part mime encoding. Whether thatโ€™s desirable or not is on a per-model basis. I feel like enumerating the details for each model, suffice to say with Gemini Web UI (not Antigravity IDE), itโ€™s best to just do a normal Ctrl+v paste and it actually stays text which is best but you canโ€™t go over a megabyte with that method with Gemini.

Pshwew!

As an attachment you can go much higher with ChatGPT. But with Claude an attachment thatโ€™s too big it reports as โ€œEmptyโ€ (regardless of models). There I go explaining it all. Thatโ€™ll be a future articleโ€ฆ maybe because itโ€™s such a moving target. Itโ€™s part of the mechanical sympathy with the Frontier LLMs. They each have their own idiosyncrasies and subtle rules. I keep it under 1MB for Gemini so even the 40K foot view is too large for it now โ€” so you can see the wisdom of pushing as much as possible into GLOSSARY.md and then not including it? Every time I do that it leaves a tiny bit of desirable storytelling scar tissue in the router, but not nearly as much as what Iโ€™ve removed and in that way we keep bringing the 40K foot view small enough to be a common size that will work with all frontier models.

So letโ€™s try dropping the glossary:

(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 (10 active line(s))

โœ… Topological Integrity Verified: 47 candidate reference(s) scanned, all exist.
   -> Executing: python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs ... [0.3033s]
   -> Ruff exit 0 (clean).
                                  ๐Ÿ“ฆ Payload Ledger (biggest first)                                  
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“
โ”ƒ File / Source                                                     โ”ƒ  Tokens โ”ƒ     Bytes โ”ƒ % Bytes โ”ƒ
โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ
โ”‚ foo_files.py                                                      โ”‚  82,785 โ”‚   330,816 โ”‚   32.0% โ”‚
โ”‚ prompt_foo.py                                                     โ”‚  48,213 โ”‚   212,314 โ”‚   20.5% โ”‚
โ”‚ ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs โ”‚  63,642 โ”‚   165,747 โ”‚   16.0% โ”‚
โ”‚ flake.nix                                                         โ”‚  33,460 โ”‚   138,803 โ”‚   13.4% โ”‚
โ”‚ /home/mike/repos/nixos/autognome.py                               โ”‚  10,581 โ”‚    47,651 โ”‚    4.6% โ”‚
โ”‚ init.lua                                                          โ”‚  10,416 โ”‚    39,888 โ”‚    3.9% โ”‚
โ”‚ apply.py                                                          โ”‚   8,584 โ”‚    36,636 โ”‚    3.5% โ”‚
โ”‚ PROMPT (checklist + prompt.md)                                    โ”‚   7,158 โ”‚    30,676 โ”‚    3.0% โ”‚
โ”‚ assets/installer/install.sh                                       โ”‚   4,257 โ”‚    16,852 โ”‚    1.6% โ”‚
โ”‚ pyproject.toml                                                    โ”‚   1,129 โ”‚     4,104 โ”‚    0.4% โ”‚
โ”‚ .gitignore                                                        โ”‚     970 โ”‚     3,598 โ”‚    0.3% โ”‚
โ”‚ __init__.py                                                       โ”‚     699 โ”‚     2,888 โ”‚    0.3% โ”‚
โ”‚ requirements.in                                                   โ”‚     675 โ”‚     2,340 โ”‚    0.2% โ”‚
โ”‚ AUTO: Recent Git Diff Telemetry                                   โ”‚     222 โ”‚       753 โ”‚    0.1% โ”‚
โ”‚ .gitattributes                                                    โ”‚      33 โ”‚        76 โ”‚    0.0% โ”‚
โ”‚ AUTO: Static Analysis Diagnostics                                 โ”‚      11 โ”‚        39 โ”‚    0.0% โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ TOTAL                                                             โ”‚ 272,835 โ”‚ 1,033,181 โ”‚  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 (222 tokens | 753 bytes)

--- Prompt Summary ---
Summed Tokens:    274,840 (from section parts)
Verified Tokens: 276,567 (from final output)
  (Difference: +1,727)
Total Words:      121,663 (content only)
Total Chars:      1,041,656
Total Bytes:      1,045,428 (UTF-8)

--- Size Perspective ---
๐Ÿ“š Equivalent in length to a **Epic Fantasy Novel** (Note: With a token/word ratio of 2.27, 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=a0161d98fff1โ€ฆ, members=3)
๐Ÿ—‚๏ธ  Rotated cartridge snapshot: foo-a0161d98-1546.zip (keeping newest 20 of 20)
Markdown output copied to clipboard
(nix) pipulate $ 

Ohhhh, almost. So close. See how that works?

Anyway for Fable 5.1 without letting it become a file-attachment it seems like it can go up to about 2MB so itโ€™s no problem giving both the 40K-foot view and the new files that create the context we need at the very same time.

Most of this turn is exposition building because Iโ€™m asking Fable 5.1 to do a lot just to make sure everything Iโ€™ve said so far is crystal clear to the New-B including explaining what that is, which is the kind of thing that comes out of the glossary, see? A lot of this is patterned after The Lord of the Rings. Good storytelling is good storytelling. Itโ€™s going to take me years of organizing and rigorous use of this system to get it up to the same level of craft thatโ€™s in his books, but that is my goal.

At this point I really want to make implementation details on this first request modest so as much can go into exposition as possible. All I want is to set good context for the next turn with anything I missed, and if I havenโ€™t missed anything then to go ahead and make the contents of that first adhocwalk.txt file that the Edit Prompt Router (epr) command brings up.

Itโ€™s going to be the New-Bโ€™s first time in NeoVim, which is essentially vim and tell them the joke about quitting vim (or vi โ€” the joke probably dates back to vi).

That Mac experience I showed up there both verbally stops abruptly and massively flips the bird at the New-B. We need to indoctrinate them as gently as we did with the less lesson during the install. Some of that may be Piper TTS dialogue, some might be commented-out lines in adhocwalk.txt. We might also want to tell them about the :Tutor command. Or even just verbally explain to them about colon-commands. Something like that but as light as possible.

This is exactly like riding a bike and we need some training wheels here and a designed โ€œletting goโ€ moment.

The Anatomy of an Overwhelming First Encounter

(nix) pipulate $ prompt
(nix) pipulate $ ahe
(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 (18 active line(s))

โœ… Topological Integrity Verified: 47 candidate reference(s) scanned, all exist.
   -> Executing: python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs ... [0.3249s]
   -> Ruff exit 0 (clean).
                                  ๐Ÿ“ฆ Payload Ledger (biggest first)                                  
โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“
โ”ƒ File / Source                                                     โ”ƒ  Tokens โ”ƒ     Bytes โ”ƒ % Bytes โ”ƒ
โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ
โ”‚ foo_files.py                                                      โ”‚  82,785 โ”‚   330,816 โ”‚   28.5% โ”‚
โ”‚ prompt_foo.py                                                     โ”‚  48,213 โ”‚   212,314 โ”‚   18.3% โ”‚
โ”‚ ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs โ”‚  63,642 โ”‚   165,747 โ”‚   14.3% โ”‚
โ”‚ flake.nix                                                         โ”‚  33,460 โ”‚   138,803 โ”‚   12.0% โ”‚
โ”‚ release.py                                                        โ”‚  14,675 โ”‚    65,186 โ”‚    5.6% โ”‚
โ”‚ PROMPT (checklist + prompt.md)                                    โ”‚  10,637 โ”‚    48,592 โ”‚    4.2% โ”‚
โ”‚ /home/mike/repos/nixos/autognome.py                               โ”‚  10,581 โ”‚    47,651 โ”‚    4.1% โ”‚
โ”‚ init.lua                                                          โ”‚  10,416 โ”‚    39,888 โ”‚    3.4% โ”‚
โ”‚ apply.py                                                          โ”‚   8,584 โ”‚    36,636 โ”‚    3.2% โ”‚
โ”‚ remotes/honeybot/nixos/configuration.nix                          โ”‚   6,068 โ”‚    23,331 โ”‚    2.0% โ”‚
โ”‚ assets/installer/install.sh                                       โ”‚   4,257 โ”‚    16,852 โ”‚    1.5% โ”‚
โ”‚ /home/mike/repos/Pipulate.com/install.md                          โ”‚   2,308 โ”‚    11,734 โ”‚    1.0% โ”‚
โ”‚ pyproject.toml                                                    โ”‚   1,129 โ”‚     4,104 โ”‚    0.4% โ”‚
โ”‚ .gitignore                                                        โ”‚     970 โ”‚     3,598 โ”‚    0.3% โ”‚
โ”‚ /home/mike/repos/Pipulate.com/_layouts/default.html               โ”‚     809 โ”‚     3,177 โ”‚    0.3% โ”‚
โ”‚ remotes/honeybot/hooks/post-receive                               โ”‚     940 โ”‚     3,080 โ”‚    0.3% โ”‚
โ”‚ __init__.py                                                       โ”‚     699 โ”‚     2,888 โ”‚    0.2% โ”‚
โ”‚ requirements.in                                                   โ”‚     675 โ”‚     2,340 โ”‚    0.2% โ”‚
โ”‚ nixops.sh                                                         โ”‚     477 โ”‚     1,730 โ”‚    0.1% โ”‚
โ”‚ remotes/honeybot/www/npvg.org/index.html                          โ”‚     331 โ”‚     1,129 โ”‚    0.1% โ”‚
โ”‚ AUTO: Recent Git Diff Telemetry                                   โ”‚     222 โ”‚       753 โ”‚    0.1% โ”‚
โ”‚ .gitattributes                                                    โ”‚      33 โ”‚        76 โ”‚    0.0% โ”‚
โ”‚ AUTO: Static Analysis Diagnostics                                 โ”‚      11 โ”‚        39 โ”‚    0.0% โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ TOTAL                                                             โ”‚ 301,922 โ”‚ 1,160,464 โ”‚  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 (222 tokens | 753 bytes)

--- Prompt Summary ---
Summed Tokens:    304,162 (from section parts)
Verified Tokens: 306,529 (from final output)
  (Difference: +2,367)
Total Words:      132,787 (content only)
Total Chars:      1,166,380
Total Bytes:      1,175,525 (UTF-8)

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

Note: Captured thought:

Thought for 7m 34s

Untangling a lengthy compiled request to find the actual ask.

Reviewing a terminal walkthrough and editor screenshot for issues.

Pinpointing why the vim mode indicator looks so confusing.

Planning a gentle onboarding tutorial for a first-time editor user.

Spotting a missing file needed to finish the task.

Drafting light-touch training wheels inside the editor experience.

Bridging familiar keys from less into vim's commands.

Checking for text-wrap risks that could corrupt long path lines.

Weighing spell-check and line-number tweaks for the router file.

Designing a headless check to verify the editor tweak.

Making sure headless output prints without extra noise.

Puzzling over an odd visual-mode cursor position in the screenshot.

Explaining curated context and the agentic-loop-versus-kata tradeoff.

Locating the source file needed to complete the patch.

Deciding where the header rewrite draft should live.

Weighing a small homepage tweak to teach the missing keys.

Deciding to keep this turn's changes to one small edit.

Deciding where the onboarding lesson should live instead.

Checking editor availability across different Mac setups.

Flagging a risky editor gap on unconfigured Mac setups.

Contrasting curated context against ad-hoc IDE search costs.

Framing the agentic loop as human-witnessed judgment already present.

Drafting friendly comment lines to teach basic vim commands.

Deciding how the router's training wheels come off later.

Designing quick probes to confirm each proposed fix.

Checking shell quoting details for the headless verification command.

Drafting a specific autocmd block for the router file.

Finalizing buffer-local resets for the router autocmd block.

Compiling the exact file list and probe commands to hand off.

Drafting a thorough exposition balancing detail with readability.

Listing each editor issue with its underlying cause.

Assembling the full response outline before writing it out.

Writing the friendly comment header for the router file.

Weighing whether editor pinning belongs in this same change.

Weighing whether a spoken hint fits the editor launch.

Verifying quoting and escaping details in the probe commands.

Confirming exact search text for the router patch.

Composing the beginner-friendly header text line by line.

Pinning the editor choice across every platform.

Contrasting sealed, reproducible setups with ad-hoc discovery.

Finalizing the precise patch block for the router file.

Double-checking the finished patch block for hidden formatting slips.

Fable 5.1 Extra: Context verified for the question this turn asks. One file is missing for the implementation you named, and Iโ€™ll say which and why before anything else: the router header you showed is written by _write_walk_router in scripts/mother_cat.py, which is not in this payload. The Piper goodbye lives in the same file. So โ€œmake the contents of that first adhocwalk.txtโ€ is a draft this turn and a patch next turn, and the draft rides in the caboose so it arrives with the writer. Everything else you asked for is answerable from whatโ€™s here, and one small patch is possible against a file that is present.

What the first epr screen says to a stranger

Reading your Mac screenshot cold, as someone who has never seen a terminal editor, there are five things on that screen, and none of them is explained by anything the stranger has been told.

The lesson stopped one door early. The walk narrates every stop, Piper says goodbye, the menu prints epr edit prompt router, and then epr opens a full-screen program with no instruction on it. The header comments in the file explain what the file is. Nothing explains what the program is, how to move in it, or how to leave. The install page did the opposite for less: it named the program before handing it over.

The editor arrives wearing your journal. VIMINIT loads the repoโ€™s init.lua, so the stranger gets the authorโ€™s daily-driver settings, and three of them are wrong for a file of paths. spell = true underlines npvg, decant, adhocwalk and UNSANITIZED in red, which a newcomer reads as errors. relativenumber = true prints the gutter as 4 3 2 1 5, which reads as nonsense. textwidth = 80 will hard-wrap a path or ! command typed past column 80, turning one router line into two and breaking the file the guard then treats as hand-edited. Your own adhoc.txt already has the textwidth release; adhocwalk.txt never got it.

The mode line reads VISUAL LINE. A keystroke already changed modes (Shift+v, or a Caps Lock v), and the stranger has no idea what that word means or that Esc is the way out. Thatโ€™s why the first rule the header teaches has to be Esc.

The page never taught j, k, q. You remembered the less lesson as teaching the three keys. The index.html in this payload says curl -fsSL https://npvg.org | less and nothing about how to move or leave inside less. Your own 2026-09-19 seed-path TODO in foo_files.py already names that gap. This matters because the vim lesson wants to say โ€œthe same j and k you used in less,โ€ and today that sentence would be false.

On a strangerโ€™s Mac itโ€™s worse than your Mac. Your Mac has nvim installed, so epr found it. The flake pins no editor (the 2026-09-16 editor TODO), so a Mac without Neovim falls through eprโ€™s chain to /usr/bin/vim, and VIMINIT="luafile ..." is an Ex command Appleโ€™s vim cannot run: INFERRED, unwitnessed, but the shape is E319 on the first keypress of the first editor a stranger ever opened. :Tutor is also a Neovim command, so the lesson below assumes the pin.

Also on that screen, and older than this ride: the headerโ€™s own first line says โ€œan edited router is never replaced,โ€ and the 2026-09-16 drill-down TODO already says the guard canโ€™t keep that promise for the one edit the header invites.

Training wheels, and where the letting-go moment lives

The bike is the keys. A stranger who read the installer in less already has j, k, and q in their hands. Vim is the same three keys with one difference: q grows a colon. Thatโ€™s the entire bridge, and itโ€™s the only thing the header needs to say in its first breath.

The training wheels are comment lines in the router itself, at the exact moment the confusion happens, per your own rule that newcomer guidance names the visible cue and the next action. The letting go is mechanical: the first router a machine ever writes carries the full header; every later router carries a two-line header with just the keys. The rider already knows whether the router existed before the ride, because the guard reads it. So the wheels come off by themselves on the second walk, and if the stranger edited the file in between, the guard keeps their file, wheels and all, because itโ€™s theirs now.

No Piper for epr. Three reasons. Your 2026-09-15 ruling says the closing adds no command and no lesson, and adding โ€œnow type eprโ€ would break it. A spoken sentence before the editor opens would cost a second of latency and repeat on every epr forever, which is the always-fires warning in audio. And the lesson belongs where the eyes are, on the screen that confused them.

The joke goes in the header because it does real work: it tells the stranger that being stuck is the normal first experience, not their failure. The form Iโ€™d use: the best random-string generator ever built is a beginner asked to exit vi. The Stack Overflow question about exiting Vim has been viewed millions of times; it is the most-visited page about a text editor on the internet, which is a fact a newcomer finds comforting.

The draft header is in the caboose, indented so it survives the fence. Itโ€™s fourteen comment lines, two path lines, and it ends with the next action: :q, then cpr, and a promise that the next walk writes the file again without the notes.

โ€œIs that it?โ€ Mostly, and you already do it

Yes, thatโ€™s the industryโ€™s answer to the bottleneck: move the human from every step to the definition of done, write โ€œwhat finished looks likeโ€ once, and let the loop run until the check passes. Itโ€™s half right, and the half it gets wrong is the half your whole system is about.

The agentic loop doesnโ€™t remove the humanโ€™s judgment; it moves it earlier, to a specification written before any evidence exists. Thatโ€™s the same shape as your own CONSENT CANNOT PRECEDE ITS OBJECT rule: an acceptance test typed at t=0 is consent to an artifact nobody has seen. And it removes the humanโ€™s practice. The person who never reads the intermediate diffs loses the ability to read a diff, which is fine until the day the loopโ€™s output is wrong and the check didnโ€™t catch it, and thereโ€™s no receipt, only โ€œbecause Claude said so.โ€

Your framework already contains the loopโ€™s good idea. THE RIDE CONTRACT declares arrival conditions at the mount. The dismount checks them against receipts. The difference is that your loop is unrolled: every iteration is a turn a human reads, and the reading is cheap because the receipts have a stable shape. A pilot doesnโ€™t audit the engine; a pilot reads instruments whose shape doesnโ€™t change from flight to flight. The straddle, the payload ledger, the ARMED โ€” 0 hit(s) line: those are instruments. The bottleneck isnโ€™t eliminated, itโ€™s converted from โ€œaudit N transformationsโ€ to โ€œread N readings,โ€ and readings are what the forgetting kata keeps small enough to read.

Which is the difference you asked whether I see. The kata is wax-on wax-off: no judgment spent, a 3% reduction a day, and the skill myelinates. This turn is the fight, and it costs the calories the kata saved. Everybody else is agentic looping; the inverse bet is that the skill the loop makes optional, reading the receipt, is the one that stays valuable, because a receipt is the only thing that outlives the model that produced it.

The honest ceiling: at some scale even receipts exceed attention. You hit it today with GLOSSARY.md, 1,183,950 bytes with it and 1,045,428 without, 45 KB over Geminiโ€™s line. This compile is 1,183,071 bytes and fits Fableโ€™s window as plain text. The glossary is the excretion organ; the router is the tissue that stays.

Not just file selection

For the New-B reading this, which is this bookโ€™s word for the newcomer at the competent middle, neither talked down to nor snowed: a capable adult who has never opened a terminal editor.

When an IDE agent sets your context, the model chooses, at run time, by searching. It greps, opens files, summarizes what it found, and each of those is a tool call that costs tokens and produces a scroll you canโ€™t easily verify. Two sessions discover differently. The frame, if it arrives at all, arrives last, after the model has already formed a picture from whatever it happened to open first.

When the context is compiled, the human chose once, and the choice is a file you can read. But youโ€™re right that the file list is the least of it. Five things ride that the IDE approach structurally cannot:

Order. The router is read by two interpreters, CPython for the paths and the model for the prose, and the prose is pedagogy. The routing note, the poem, the chapter list, and the constitution arrive before a single line of code. The frame is encoded twice, in the declared map and in the realized files, so the marble rolls home wherever it lands.

The metal. flake.nix, configuration.nix, requirements.in, and __init__.py tell the model exactly what runs: Determinate Nix 3.19.1 over 2.34.6, Python 3.12.13, v2.58, 291 packages, chromium 150 paired with its driver, a Mac that needs no --impure. A model whose sandbox is Docker canโ€™t replay that, and it doesnโ€™t need to. What it gains is the collapse of ambiguity: it never has to guess which of six plausible setups this is, because the file says which. Most hallucination in coding assistance is the model resolving an ambiguity the human never noticed was open.

Receipts. The ! lines are stdout captured on the operatorโ€™s machine during this compile, indexed in the Manifest as the sole authority on what ran. The model reads a fact it never fetched and can cite the section marker it came from.

The seal. Same input, same bytes, same hash, in a zip a stranger can verify with the standard library. Discovery at run time has no hash.

The anvil. This is the one you named. Your code looks like FastAPI to a prior: decorators, routes, async handlers. A model handed a bare repo root reaches for FastAPI idioms, Pydantic models, Jinja templates, a docker-compose.yml, because thatโ€™s where the mass of its training sits. The 2024-11-18 PATTERN ALERT article is the receipt that this happens. The compile repeats the counter-claim until the prior loses: Nix over Docker, HTMX over React, in the poem, in the chapter titles, in the flakeโ€™s own comments. Thatโ€™s the gravity well you can see the lensing on, and the metaphor pays rent because the prediction is checkable: the same question, asked with IDE-discovered context and with a compiled cartridge, produces answers that assume versus answers that cite and refuse. The blind fan-out articles are where that measurement lives.

The cost is the one the Gemini editorial named. The human pays attention per compile, by hand. Thatโ€™s not a flaw in the method; itโ€™s the method, and the forgetting kata is how the bill stays payable.

1. PROBES

The first line locates the router writer and the goodbye in the file the next turn needs. The second is the straddle for the one patch below: BEFORE reads tw=80 spell=true rnu=true, AFTER reads tw=0 spell=false rnu=false; a fresh headless Neovim reads init.lua at call time, so no ignition is needed. The third and fourth are CENSUS lines, identical before and after: the page does not teach the three keys (0), and the flake does not pin an editor (0, printed by grep with a nonzero exit).

rg -n 'Written by the last completed walk|def _write_walk_router|Goodbye' scripts/mother_cat.py
VIMINIT='luafile init.lua' nvim --headless adhocwalk.txt -c 'lua io.write(string.format("tw=%d spell=%s rnu=%s\n", vim.bo.textwidth, tostring(vim.wo.spell), tostring(vim.wo.relativenumber)))' -c 'qa!'
grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
grep -c 'neovim' flake.nix

2. NEXT CONTEXT

Keep the router you compiled this turn (the 40K-foot view fits Fableโ€™s window) and add these lines beneath it. init.lua, flake.nix and the padโ€™s index.html are already there.

# --- THE NEWCOMER'S FIRST VIM: the router writer and the menu that names epr ---
scripts/mother_cat.py
scripts/boot_menu.py
! rg -n 'Written by the last completed walk|def _write_walk_router|Goodbye' scripts/mother_cat.py
! VIMINIT='luafile init.lua' nvim --headless adhocwalk.txt -c 'lua io.write(string.format("tw=%d spell=%s rnu=%s\n", vim.bo.textwidth, tostring(vim.wo.spell), tostring(vim.wo.relativenumber)))' -c 'qa!'
! grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
! grep -c 'neovim' flake.nix

3. PATCHES

One car, against a file in this payload. It fixes the three things the journal config does to a router buffer, buffer-locally, and touches nothing you use on adhoc.txt. The header rewrite, the pageโ€™s three keys, and the editor pin are one fence next turn, because each depends on the others.

Car 1: the router buffer is a list, not prose.

Target: init.lua
[[[SEARCH]]]
-- adhoc.txt: margins permanently released (masthead art, wide-format work)
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
    pattern = "adhoc.txt",
    callback = function()
        vim.opt_local.textwidth = 0
    end,
})
[[[DIVIDER]]]
-- adhoc.txt: margins permanently released (masthead art, wide-format work)
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
    pattern = "adhoc.txt",
    callback = function()
        vim.opt_local.textwidth = 0
    end,
})

-- adhocwalk.txt: the router a walk writes and `epr` opens, which is a
-- newcomer's first vim (2026-09-20, read off the first Mac epr screen).
-- THE ROUTER IS A LIST, NOT PROSE: every line is a path or a `! ` command,
-- so the journal defaults above are wrong here in three ways a newcomer sees
-- at once. spell paints every path red, which reads as errors. textwidth=80
-- hard-wraps a path or command typed past the column, turning one router
-- line into two and breaking it. relativenumber counts distance from the
-- cursor, which reads as 4 3 2 1 5 to someone meeting it cold. Three
-- buffer-local resets, nothing global; adhoc.txt keeps its own release above.
vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
    pattern = "adhocwalk.txt",
    callback = function()
        vim.opt_local.textwidth = 0
        vim.opt_local.spell = false
        vim.opt_local.relativenumber = false
    end,
})
[[[REPLACE]]]

Ignition: none required for the compile-lane witness; the echoed probe starts a fresh Neovim that reads init.lua at call time. For a session you already have open, <F2>. The Mac picks it up on its next nix develop, which pulls the commit before the next epr.

4. PROMPT

The router is the newcomer's first vim, and this turn carries the file that writes it. Three cars, all small, riding in one fence because each depends on the others: the header says j and k were met in less, so the page must teach them; the header names :Tutor, so the editor must be Neovim on every platform. The init.lua buffer-local resets landed last turn; the second echoed probe is their AFTER.

Car 1, scripts/mother_cat.py, _write_walk_router: record whether the router existed BEFORE the ride starts, not at write time, because the rider writes it twice per ride (once after ARCHIVE STATUS, once after DECANT). A router that did not exist before the ride gets the training-wheels header; every later write gets the short header. Replace "Edit freely: an edited router is never replaced" with the true sentence, "The next walk rewrites this file unless you have edited it," which also discharges the header half of the 2026-09-16 drill-down TODO. The guard is untouched. Piper's goodbye is untouched (the 2026-09-15 ruling: the closing adds no command and no lesson).

Training-wheels header, comment lines only, the writer fills the two path lines:

    # This is vim. Nothing here needs typing today.
    # Two keys from less still work: j moves down, k moves up.
    # Leaving takes one more key than less did: type  :q  and press Enter.
    # Every colon-command works that way. Esc backs out of anything else.
    # (The old joke: the best random-string generator is a beginner asked to exit vi.)
    #
    # This file is your prompt router: one line per thing the AI will see.
    # A file path, or a command after `! `. Lines starting with # are skipped.
    # The checked preview rides. The whole UNSANITIZED archive is the commented line beneath it.
    /Users/michaellevin/npvg/data/decant-preview.md
    # /Users/michaellevin/npvg/data/captures/walk-7_r2ybpy/captures.md
    #
    # To change a line: i starts typing, Esc stops, u undoes. :wq saves and quits; :q! quits without saving.
    # Thirty minutes of practice whenever you want it: type  :Tutor  and press Enter.
    # Next: :q  then  cpr  at the prompt. The next walk writes this file again without these notes.

Short header, every later write:

    # Written by the last completed walk. Keys: j down, k up, :q quits, :wq saves and quits.
    # One line per thing the AI should see: a file path, or a command after `! `.
    # The checked preview rides. The whole UNSANITIZED archive is the commented line beneath it.
    # The next walk rewrites this file unless you have edited it.

Car 2, remotes/honeybot/www/npvg.org/index.html: one sentence after the less line, "Inside less, j moves down, k moves up, and q quits." Fix "The first commands lets you" to "The first command lets you" in the same car. Ignition: ./nixops.sh, no rebuild. AFTER: the grep -ciE probe reads 1, and a curl with a non-curl user agent against the pad reads the sentence (the door serves the page only when the agent is not curl or wget).

Car 3, flake.nix commonPackages: neovim, one line, so epr, :Tutor and VIMINIT's luafile are the same editor on Linux, macOS and WSL; today a Mac without nvim falls to /usr/bin/vim, where luafile is INFERRED to error at load. Gate before it rides: nix eval --raw .#devShells.aarch64-darwin.quiet.drvPath and the x86_64-linux twin both evaluate. Ignition: exit then nix develop. AFTER: grep -c 'neovim' flake.nix reads 1 (2 if the comment names it: predict the epitaph).

Straddle for Car 1: import _write_walk_router into a throwaway directory with the pre-existence flag forced both ways and count comment lines in each output; name the exact spelling once the function's signature is in context. Do not ride the trail, Piper, or the menu copy; scripts/boot_menu.py is here only so the epr line can name the editor if that reads as a one-word change and not a lesson.

5. EXTERNAL DELIVERABLES

None this turn outside the repo.

MikeLev.in: After all that I missed one file, ha ha!

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:

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 $ rg -n 'Written by the last completed walk|def _write_walk_router|Goodbye' scripts/mother_cat.py
VIMINIT='luafile init.lua' nvim --headless adhocwalk.txt -c 'lua io.write(string.format("tw=%d spell=%s rnu=%s\n", vim.bo.textwidth, tostring(vim.wo.spell), tostring(vim.wo.relativenumber)))' -c 'qa!'
grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
grep -c 'neovim' flake.nix
338:def _write_walk_router(archive_path, preview_path=None):
375:        "# Written by the last completed walk. Edit freely: an edited router is never replaced.\n"
1038:                "Review any summary before sharing it. Goodbye."
1041:                "Read the results in your terminal. Goodbye."
init.lua loaded successfully!tw=80 spell=true rnu=true
0
0
(nix) pipulate $ 

2: Context:

# 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    _   _   _               ____ _   _  ___  ____  _   
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  This is *exactly like riding a bike* and we need some training wheels here and a designed "letting go" moment. Let's take good care of the New-B's. ***WE NEED TO HAVE A LOT OF EMPATHY WITH THEM***.
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  That looks like a very small context. Are you sure that's enough? To I have to be explicit about re-including the ones you know I'm going to be editing out on turn 2 from the 40K-FT VIEW?
# 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)
# assets/installer/install.sh # <-- Pipulate.com installer real home in github/pipulate repo
# 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 (Edit-in selections from above and add new files immediately below)
# /home/mike/repos/grimoire/_posts/2026-09-20-flatnotes-trilium-parity-321-ledger-replayable-backups-nixos.md
# ~/repos/nixos/autognome.py                 #  <-- More rare to have to include, but the true "top" of the muscle memory stack for day-to-day purposes
# ~/repos/nixos/configuration.nix            #  <-- "Global" IaC context (most of you won't have)
# ~/repos/nixos/blogs.nix
# ~/repos/nixos/packages.nix                 #  <-- Full disclosure on pre-flake IaC available apps.
# ~/repos/nixos/services.nix                 #  <-- Running Linux system services.
# ~/repos/nixos/ai-acceleration.nix          #  <-- Paid a lot for your hardware? We've got you covered.
# ~/repos/nixos/hardware-configuration.nix   #  <-- Automatically generated by Nix. The ultimate in IaC transparency.
# ~/repos/nixos/flatnotes.nix                #  <-- One easy local way to edit all your markdown in a browser
# ~/repos/nixos/openclaw.nix                 #  <-- Available but usually disabled (because I don't want to be)
# ~/repos/nixos/scripts/backup-nix.sh        #  <-- Nix is already a `/nix/store/` "backup". This backs that up.
# ~/repos/nixos/scripts/backup-essential.py  #  <-- Reconstitute much of your system from your keychain
# ~/repos/nixos/scripts/backup-force.py      #  <-- The occasional "big sync" to get thing2 identical to thing1
# ~/repos/nixos/scripts/backup-home.py       #  <-- The fast, easy, frequent backup for 80/20-rule data safety
# ~/repos/nixos/scripts/backup-things.py     #  <-- rsync'ing from thing1 to thing2, can be time consuming.
# flake.nix

# Context 2
# /home/mike/repos/nixos/scripts/dedupe.py
# /home/mike/repos/nixos/configuration.nix
# /home/mike/repos/nixos/scripts/backup-home.py
# ! env -i /home/mike/repos/pipulate/.venv/bin/python -c "import sys, sqlite3; print(sys.version.split()[0], 'sqlite', sqlite3.sqlite_version); import PIL.Image; print('PIL', PIL.__version__)"
# ! f=$(mktemp /tmp/dedupe-gio.XXXXXX) && LD_LIBRARY_PATH= gio trash "$f"; echo "gio_exit=$? still_there=$([ -e "$f" ] && echo yes || echo no) gio=$(command -v gio)"
# ! bash -ic 'type dedupe' 2>&1 | tail -1
# ! /home/mike/repos/pipulate/.venv/bin/python -c "import os, shutil; from pathlib import Path; from PIL import Image; H = '/tmp/dedupe-smoke'; R = H + '/root'; shutil.rmtree(H, ignore_errors=True); os.makedirs(R + '/sub'); g = Image.linear_gradient('L').point(lambda v: v // 2 + 40); a = Image.merge('RGB', (g, g.rotate(90), g.rotate(180))).resize((64, 48)); b = Image.merge('RGB', (g, g.rotate(-90), g.rotate(180))).resize((64, 48)); a.save(R + '/a_orig.png'); shutil.copy(R + '/a_orig.png', R + '/a_copy.png'); b.save(R + '/sub/b.jpg', quality=95); b.save(R + '/sub/b_resaved.jpg', quality=70); Image.new('RGB', (64, 48), (200, 30, 30)).save(R + '/c_unique.png'); Path(R + '/blob1.bin').write_bytes(b'x' * 5000); shutil.copy(R + '/blob1.bin', R + '/blob2.bin'); [os.utime(R + p, (1e9, 1e9)) for p in ('/a_orig.png', '/sub/b.jpg', '/blob1.bin')]; print('fixture:', sorted(os.listdir(R)), sorted(os.listdir(R + '/sub')))"
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root
# ! find /tmp/dedupe-smoke/root -type f | sort; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --top

# Context 3
# /home/mike/repos/nixos/scripts/dedupe.py
# /home/mike/repos/nixos/configuration.nix
# ! ls -d /mnt/thing1/*/ ~/Pictures 2>&1 | head -40
# ! f=$(mktemp /mnt/thing1/dedupe-gio.XXXXXX) && LD_LIBRARY_PATH= gio trash "$f"; echo "gio_exit=$? still_there=$([ -e "$f" ] && echo yes || echo no)"; ls -d /mnt/thing1/.Trash-1000 2>&1
# ! /home/mike/repos/pipulate/.venv/bin/python -c "import os, shutil; from pathlib import Path; from PIL import Image; H = '/tmp/dedupe-smoke'; R = H + '/root'; shutil.rmtree(H, ignore_errors=True); os.makedirs(R + '/sub'); g = Image.linear_gradient('L').point(lambda v: v // 2 + 40); a = Image.merge('RGB', (g, g.rotate(90), g.rotate(180))).resize((64, 48)); b = Image.merge('RGB', (g, g.rotate(-90), g.rotate(180))).resize((64, 48)); a.save(R + '/a_orig.png'); shutil.copy(R + '/a_orig.png', R + '/a_copy.png'); b.save(R + '/sub/b.jpg', quality=95); b.save(R + '/sub/b_resaved.jpg', quality=70); Image.new('RGB', (64, 48), (200, 30, 30)).save(R + '/c_unique.png'); Path(R + '/blob1.bin').write_bytes(b'x' * 5000); shutil.copy(R + '/blob1.bin', R + '/blob2.bin'); [os.utime(R + p, (1e9, 1e9)) for p in ('/a_orig.png', '/sub/b.jpg', '/blob1.bin')]; print('fixture:', sorted(os.listdir(R)), sorted(os.listdir(R + '/sub')))"
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root
# ! find /tmp/dedupe-smoke/root -type f | sort; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --top
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --where img1:ffffffffffffffff:64x48:666

# Context 4
# /home/mike/repos/nixos/scripts/dedupe.py
# /home/mike/repos/nixos/configuration.nix
# ! /home/mike/repos/pipulate/.venv/bin/python -c "import os, shutil; from pathlib import Path; from PIL import Image; H = '/tmp/dedupe-smoke'; R = H + '/root'; shutil.rmtree(H, ignore_errors=True); os.makedirs(R + '/sub'); g = Image.linear_gradient('L').point(lambda v: v // 2 + 40); a = Image.merge('RGB', (g, g.rotate(90), g.rotate(180))).resize((64, 48)); b = Image.merge('RGB', (g, g.rotate(-90), g.rotate(180))).resize((64, 48)); a.save(R + '/a_orig.png'); shutil.copy(R + '/a_orig.png', R + '/a_copy.png'); b.save(R + '/sub/b.jpg', quality=95); b.save(R + '/sub/b_resaved.jpg', quality=70); Image.new('RGB', (64, 48), (200, 30, 30)).save(R + '/c_unique.png'); Path(R + '/blob1.bin').write_bytes(b'x' * 5000); shutil.copy(R + '/blob1.bin', R + '/blob2.bin'); [os.utime(R + p, (1e9, 1e9)) for p in ('/a_orig.png', '/sub/b.jpg', '/blob1.bin')]; print('fixture:', sorted(os.listdir(R)), sorted(os.listdir(R + '/sub')))"
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root
# ! find /tmp/dedupe-smoke/root -type f | sort; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --top
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --where img1:ffffffffffffffff:64x48:666
# ! LD_LIBRARY_PATH= script -qec "PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run" /dev/null | tr '\r' '\n' | grep -c "$(printf '\033')\[2K"; echo "script=$(command -v script)"

# Context 4
# /home/mike/repos/nixos/scripts/dedupe.py
# /home/mike/repos/nixos/configuration.nix
# ! /home/mike/repos/pipulate/.venv/bin/python -c "import os, shutil; from pathlib import Path; from PIL import Image; H = '/tmp/dedupe-smoke'; R = H + '/root'; shutil.rmtree(H, ignore_errors=True); os.makedirs(R + '/sub'); g = Image.linear_gradient('L').point(lambda v: v // 2 + 40); a = Image.merge('RGB', (g, g.rotate(90), g.rotate(180))).resize((64, 48)); b = Image.merge('RGB', (g, g.rotate(-90), g.rotate(180))).resize((64, 48)); a.save(R + '/a_orig.png'); shutil.copy(R + '/a_orig.png', R + '/a_copy.png'); b.save(R + '/sub/b.jpg', quality=95); b.save(R + '/sub/b_resaved.jpg', quality=70); Image.new('RGB', (64, 48), (200, 30, 30)).save(R + '/c_unique.png'); Path(R + '/blob1.bin').write_bytes(b'x' * 5000); shutil.copy(R + '/blob1.bin', R + '/blob2.bin'); [os.utime(R + p, (1e9, 1e9)) for p in ('/a_orig.png', '/sub/b.jpg', '/blob1.bin')]; print('fixture:', sorted(os.listdir(R)), sorted(os.listdir(R + '/sub')))"
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run
# ! LD_LIBRARY_PATH= script -qec "PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root" /dev/null | tr '\r' '\n' > /tmp/dedupe-pty.txt; echo "redraws=$(grep -c "$(printf '\033')\[2K" /tmp/dedupe-pty.txt) trashed_lines=$(grep -c 'trashed /tmp' /tmp/dedupe-pty.txt)"; grep -o 'pass [0-9]*: .*' /tmp/dedupe-pty.txt
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root
# ! find /tmp/dedupe-smoke/root -type f | sort; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --top
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --where img1:ffffffffffffffff:64x48:666
# ! (cd /home/mike/repos/nixos/scripts && /home/mike/repos/pipulate/.venv/bin/python -c "import dedupe as d; R = '/mnt/thing1/Sort Big/New/'; f = lambda a, b: (lambda p, q: print(a, 'vs', b, 'max', max(abs(x - y) for x, y in zip(p, q)), 'cells_over', sum(abs(x - y) > d.CONFIRM_TOLERANCE for x, y in zip(p, q))))(d.thumb(R + a), d.thumb(R + b)); [f(a, b) for a, b in (('IMG_4029.PNG', 'IMG_4029-1.PNG'), ('IMG_3128.PNG', 'IMG_3129.PNG'), ('682278670.JPG', '682278670-1.JPG'), ('IMG_6507.JPG', 'IMG_6508.JPG'), ('IMG_9556.PNG', 'IMG_9557.PNG'), ('IMG_3282.PNG', 'IMG_3283.PNG'))]")

# Context 1
# release.py                                 # <-- The deploy process
# assets/installer/install.sh                # <-- Pipulate.com installer real home in github/pipulate repo
# ~/repos/Pipulate.com/_layouts/default.html
# ~/repos/Pipulate.com/install.md            # <-- Gets copied into place here by pipulate/release.py
# nixops.sh                                  # <-- You've heard of GitOPs? Well, this is NixOPs. 
# remotes/honeybot/hooks/post-receive        # <-- Ever hear of GitHub Pages? Or github.io? This is that.
# remotes/honeybot/nixos/configuration.nix   # <-- It's as if Pipulate had kids. Spy kids.
# remotes/honeybot/www/npvg.org/index.html   # <-- New "Pipulate" install site under operation stick bug

# Context 2
# --- THE NEWCOMER'S FIRST VIM: the router writer and the menu that names epr ---
scripts/mother_cat.py
scripts/boot_menu.py
! rg -n 'Written by the last completed walk|def _write_walk_router|Goodbye' scripts/mother_cat.py
! VIMINIT='luafile init.lua' nvim --headless adhocwalk.txt -c 'lua io.write(string.format("tw=%d spell=%s rnu=%s\n", vim.bo.textwidth, tostring(vim.wo.spell), tostring(vim.wo.relativenumber)))' -c 'qa!'
! grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
! grep -c 'neovim' flake.nix

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

Is that enough context Fable 5.1? Do you see my point about how after turn 1 Iโ€™m always editing out the whole block of 40K-Foot View stuff? Is that an issue here?

3: Patches:

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
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'init.lua'.
(nix) pipulate $ d
diff --git a/init.lua b/init.lua
index 04478ec1..8f66974d 100644
--- a/init.lua
+++ b/init.lua
@@ -59,6 +59,24 @@ vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
     end,
 })
 
+-- adhocwalk.txt: the router a walk writes and `epr` opens, which is a
+-- newcomer's first vim (2026-09-20, read off the first Mac epr screen).
+-- THE ROUTER IS A LIST, NOT PROSE: every line is a path or a `! ` command,
+-- so the journal defaults above are wrong here in three ways a newcomer sees
+-- at once. spell paints every path red, which reads as errors. textwidth=80
+-- hard-wraps a path or command typed past the column, turning one router
+-- line into two and breaking it. relativenumber counts distance from the
+-- cursor, which reads as 4 3 2 1 5 to someone meeting it cold. Three
+-- buffer-local resets, nothing global; adhoc.txt keeps its own release above.
+vim.api.nvim_create_autocmd({"BufRead", "BufNewFile"}, {
+    pattern = "adhocwalk.txt",
+    callback = function()
+        vim.opt_local.textwidth = 0
+        vim.opt_local.spell = false
+        vim.opt_local.relativenumber = false
+    end,
+})
+
 --  _____                 _   _                 
 -- |  ___|   _ _ __   ___| |_(_) ___  _ __  ___ 
 -- | |_ | | | | '_ \ / __| __| |/ _ \| '_ \/ __|
(nix) pipulate $ m
git push
๐Ÿ“ Committing: chore: Adjust adhocwalk.txt handling in init.lua 
[main 683c1dab] chore: Adjust adhocwalk.txt handling in init.lua
 1 file changed, 18 insertions(+)
(nix) pipulate $ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 48 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 799 bytes | 799.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:pipulate/pipulate.git
   90dc556d..683c1dab  main -> main
(nix) pipulate $ 

Ooh, my favorite kind of ignition, an F2 here in NeoVim. Okay, F2! Oh, that doesnโ€™t matter here on my Linux dev machine. Iโ€™ve got to do the whole Mac install. Sigh, okay.

4: Prompt:

The router is the newcomerโ€™s first vim, and this turn carries the file that writes it. Three cars, all small, riding in one fence because each depends on the others: the header says j and k were met in less, so the page must teach them; the header names :Tutor, so the editor must be Neovim on every platform. The init.lua buffer-local resets landed last turn; the second echoed probe is their AFTER.

Car 1, scripts/mother_cat.py, _write_walk_router: record whether the router existed BEFORE the ride starts, not at write time, because the rider writes it twice per ride (once after ARCHIVE STATUS, once after DECANT). A router that did not exist before the ride gets the training-wheels header; every later write gets the short header. Replace โ€œEdit freely: an edited router is never replacedโ€ with the true sentence, โ€œThe next walk rewrites this file unless you have edited it,โ€ which also discharges the header half of the 2026-09-16 drill-down TODO. The guard is untouched. Piperโ€™s goodbye is untouched (the 2026-09-15 ruling: the closing adds no command and no lesson).

Training-wheels header, comment lines only, the writer fills the two path lines:

# This is vim. Nothing here needs typing today.
# Two keys from less still work: j moves down, k moves up.
# Leaving takes one more key than less did: type  :q  and press Enter.
# Every colon-command works that way. Esc backs out of anything else.
# (The old joke: the best random-string generator is a beginner asked to exit vi.)
#
# This file is your prompt router: one line per thing the AI will see.
# A file path, or a command after `! `. Lines starting with # are skipped.
# The checked preview rides. The whole UNSANITIZED archive is the commented line beneath it.
/Users/michaellevin/npvg/data/decant-preview.md
# /Users/michaellevin/npvg/data/captures/walk-7_r2ybpy/captures.md
#
# To change a line: i starts typing, Esc stops, u undoes. :wq saves and quits; :q! quits without saving.
# Thirty minutes of practice whenever you want it: type  :Tutor  and press Enter.
# Next: :q  then  cpr  at the prompt. The next walk writes this file again without these notes.

Short header, every later write:

# Written by the last completed walk. Keys: j down, k up, :q quits, :wq saves and quits.
# One line per thing the AI should see: a file path, or a command after `! `.
# The checked preview rides. The whole UNSANITIZED archive is the commented line beneath it.
# The next walk rewrites this file unless you have edited it.

Car 2, remotes/honeybot/www/npvg.org/index.html: one sentence after the less line, โ€œInside less, j moves down, k moves up, and q quits.โ€ Fix โ€œThe first commands lets youโ€ to โ€œThe first command lets youโ€ in the same car. Ignition: ./nixops.sh, no rebuild. AFTER: the grep -ciE probe reads 1, and a curl with a non-curl user agent against the pad reads the sentence (the door serves the page only when the agent is not curl or wget).

Car 3, flake.nix commonPackages: neovim, one line, so epr, :Tutor and VIMINITโ€™s luafile are the same editor on Linux, macOS and WSL; today a Mac without nvim falls to /usr/bin/vim, where luafile is INFERRED to error at load. Gate before it rides: nix eval โ€“raw .#devShells.aarch64-darwin.quiet.drvPath and the x86_64-linux twin both evaluate. Ignition: exit then nix develop. AFTER: grep -c โ€˜neovimโ€™ flake.nix reads 1 (2 if the comment names it: predict the epitaph).

Straddle for Car 1: import _write_walk_router into a throwaway directory with the pre-existence flag forced both ways and count comment lines in each output; name the exact spelling once the functionโ€™s signature is in context. Do not ride the trail, Piper, or the menu copy; scripts/boot_menu.py is here only so the epr line can name the editor if that reads as a one-word change and not a lesson.

5: Deliverables:

Okay, let me test this on the Mac. But for a good Mac test we need to do a full release.

(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 $ vim __init__.py 
(nix) pipulate $ d
diff --git a/__init__.py b/__init__.py
index cc287966..f8ca6d7d 100644
--- a/__init__.py
+++ b/__init__.py
@@ -12,7 +12,7 @@ Usage:
     pipulate
 """
 
-__version__ = "2.58"
+__version__ = "2.59"
 # APOSTROPHES RESTORED (2026-08-04). They were stripped as a workaround for
 # flake.nix's descMatch regex, whose character class excluded ' from the
 # CAPTURE and truncated the banner to "(So)". That regex was fixed in the same
@@ -23,7 +23,7 @@ __version__ = "2.58"
 # is not a property of the system; the regex is. Blast radius is one banner:
 # nothing but flake.nix reads this name -- version_sync.py syncs __version__
 # and __description__, never this. So'wI' chu' -- "engage the cloaking device."
-__version_description__ = "NPvg.org 1st 10 Minutes"
+__version_description__ = "NPvg.org New-B Empathy"
 # SPDX expression, single source of truth, synced into pyproject.toml by
 # scripts/release/version_sync.py. "-or-later" (not bare AGPL-3.0, which is
 # deprecated SPDX) because the header below grants "any later version".
(nix) pipulate $ m
๐Ÿ“ Committing: fix: bump __version__ and __version_description__
[main 5413cf6f] fix: bump __version__ and __version_description__
 1 file changed, 2 insertions(+), 2 deletions(-)
(nix) pipulate $ 
(nix) pipulate $ release
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ๐Ÿฐ 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           |__>-\_>_>  \____/                     ๐Ÿฅ•๐Ÿฅ•๐Ÿฅ•                                    โ”‚
โ”‚                                                                                                                             โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
๐Ÿ“‹ Current version: 2.59
โœ… Updated pyproject.toml (version and description)
โœ… Pushed AI_CONTEXT.md update to Pipulate.com repo.
[main d639025b] docs(ai-context): Add new AI context links and update index count
โœ… Pushed 683c1dab..d639025b  main -> main
Successfully built pipulate-2.59.tar.gz and pipulate-2.59-py3-none-any.whl
๐ŸŽ‰ Published 2.59 -> https://pypi.org/project/pipulate/2.59/

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ๐ŸŽ‰ Release Pipeline Complete โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                                                                                             โ”‚
โ”‚                                                 ๐ŸŽ‰ Pipulate Release Summary                                                 โ”‚
โ”‚  โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ  โ”‚
โ”‚  โ”‚ Component                 โ”‚ Details                                                                    โ”‚    Status    โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ ๐Ÿค– gemma3:latest Message  โ”‚ docs(ai-context): Add new AI context links and update index count          โ”‚      โœจ      โ”‚  โ”‚
โ”‚  โ”‚                           โ”‚                                                                            โ”‚ gemma3:lateโ€ฆ โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ ๐Ÿ“ฆ Version                โ”‚ 2.59                                                                       โ”‚    โœ… Set    โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ ๐Ÿš€ PyPI Release           โ”‚ https://pypi.org/project/pipulate/2.59/                                    โ”‚   โœ… Live    โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ โฐ Completed              โ”‚ 2026-09-20 16:35:34                                                        โ”‚   ๐ŸŽฏ Done    โ”‚  โ”‚
โ”‚  โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ  โ”‚
โ”‚                                                                                                                             โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
๐Ÿ”„ server.py touched; the watchdog restarts the server.
(nix) pipulate $

And now over to the Macโ€ฆ

Where it says โ€œVoice ready.โ€ it also needs to print on the screen โ€œMake sure your volume is turned up.โ€

Upfff! Itโ€™s still so abrupt there and the end with the Piper TTS dialogue after capture 3 of 3 is done. Thereโ€™s not even a mention of expectations at that point. They need to understand:

  1. A bunch of stuff has been captured and is on disk.
  2. The next step is typing epr to enter NeoVim where you will see these files listed. That is the router of the context compiler. You add lines here.
  3. To quit NeoVim, type colon q enter. Colon puts you in command mode and q is the command for quit. If you made any changes to the file use colon q exclamation point enter to exit without saving your changes. If you got yourself into insert mode you have to hit the Esc key before hitting the colon.

Or something like that. You get the point. We have to just get them over that first vi / vim / nvim hurdle per the joke. Thatโ€™s the training wheels. Thatโ€™s the edit you couldnโ€™t make on this turn I think, but this is good for reiterating what I want for when you do make the edit.

Install and capture look like this on the Mac, which is pretty awesome but for the details I mentioned above.

(nix:nix-shell-env) (nix) npvg $ exit
exit

Saving session...
...saving history...truncating history files...
...completed.
michaellevin@MichaelMacBook-Pro ~ % cd ~/                             
michaellevin@MichaelMacBook-Pro ~ % rm -rf npvg
michaellevin@MichaelMacBook-Pro ~ % rm -rf ~/.config/pipulate         
michaellevin@MichaelMacBook-Pro ~ % curl -fsSL https://npvg.org | bash
Npvg -> ~/npvg   (to remove it later: rm -rf ~/npvg)
Deploy key saved to .ssh/rot (public, pull-only: it lets this folder fetch updates without a GitHub account).
To come back later:  cd ~/npvg && nix develop
Hydrating the Nix environment (the first time can take a few minutes)...
Restored session: Sun Sep 20 16:38:36 EDT 2026
โœ… Transformed into a git repository (pre-transform files backed up to /tmp/nix-shell.WS3im3/tmp.2L7qRPGJhq).
Already up to date.
Found ~/.ssh/id_rsa -- switching the git remote to SSH so auto-updates use it.
(Determinate Nix 3.19.1) 2.34.6 ยท Python 3.12.13 ยท v2.59 ยท ~/npvg
๐Ÿ”ง Fresh install detected โ€” packages downloading...
โœ… 291 packages ready.
๐Ÿ““ 12 starter file(s) copied into Notebooks/ -- yours to edit.

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:nix-shell-env) (nix) npvg $ walk
Trail resolved: assets/trails/public_walk.json

This walk opens three public pages. Nothing to sign in to.
Return here and type CAPTURE when prompted at each page.
After all three captures and successful checks, it saves a private summary
and replaces your clipboard. Over SSH it uses a bridge file.
Nothing is sent to a chatbot. Review the summary before sharing it.

Choose a walk:
  1  Practice - read the steps; no pages open.
  2  Start the walk - open the pages.
  q  Exit (Enter also exits).
Choice: 2
Riding trail 'public_walk' -- 3 stop(s).

Make sure your audio is turned on.

The steps can be read aloud.

The voice is Piper, a small text-to-speech program that runs on this
computer; it was made for a Raspberry Pi. It reads sentences a person
typed in advance. No AI writes them, and nothing you say or type is
sent anywhere. Answering yes downloads the voice once, about 60 MB.

Read the steps aloud? [y/N] y

This walk opens three pages. You do not need to click anything. At each page, return here and wait for the CAPTURE prompt. Type CAPTURE and press Enter.
Downloading the voice, about 60 MB, once...
Voice ready.

Summary file: data/decant-preview.md (private; replaced on save).
Checks can miss private details. A blocked check leaves the older file alone.

-- [ ] - [ ] -- Stop 1/3: the_word ---

Opening page one in the browser. Leave the browser open; it closes on its own after capture. When the page is ready, return here and type CAPTURE.
Opening the browser; waiting for the page...

The page is open. When it looks finished, come back to the terminal and type CAPTURE.
Any other response aborts without capturing artifacts.
CAPTURE> CAPTURE
  LOCAL ARCHIVE  /Users/michaellevin/npvg/data/captures/walk-lzgidt2m/captures.md  (directory 0700, file 0600)
  Captured. final_url=https://npvg.org/walk/1/ artifacts=12
  ADVANCE -> next stop.

-- [ ] - [ ] -- Stop 2/3: the_receipt ---

Opening page two in the browser. Leave the browser open; it closes on its own after capture. When the page is ready, return here and type CAPTURE.
Opening the browser; waiting for the page...

The page is open. When it looks finished, come back to the terminal and type CAPTURE.
Any other response aborts without capturing artifacts.
CAPTURE> CAPTURE
  Captured. final_url=https://npvg.org/walk/2/ artifacts=12
  ADVANCE -> next stop.

-- [ ] - [ ] -- Stop 3/3: the_two_pages ---

Opening the last page in the browser. Leave the browser open; it closes on its own after capture. When the page is ready, return here, type CAPTURE, and read the result.
Opening the browser; waiting for the page...

The page is open. When it looks finished, come back to the terminal and type CAPTURE.
Any other response aborts without capturing artifacts.
CAPTURE> CAPTURE
  Captured. final_url=https://npvg.org/walk/3/ artifacts=12
  ARCHIVE STATUS  complete

Local archive file line for context.md or adhoc.txt:
/Users/michaellevin/npvg/data/captures/walk-lzgidt2m/captures.md
Review locally before compiling; raw bytes are not a safe disclosure.
  WALK ROUTER  /Users/michaellevin/npvg/adhocwalk.txt  (0600; names the UNSANITIZED archive until a preview is released)

Ride complete. Every stop produced a capture receipt.

Checking the summary before saving it and trying the clipboard.
   Preview checks: substitutions=0 denylist=0 secrets=0
   LOCAL PREVIEW /Users/michaellevin/npvg/data/decant-preview.md (0600; sha256=80d984b53c0f8b90c1c04d6e009057f74ea27817a0336b00807c1759bad7b730)
   WALK ROUTER  /Users/michaellevin/npvg/adhocwalk.txt  (0600; the preview rides, the archive is commented beneath it)
Markdown output copied to clipboard
   Read the save and copy messages above; either step can fail.
   Review the summary before sharing it. You choose what to send.

The three-page walk is finished. Read the save and copy results in your terminal. Review any summary before sharing it. Goodbye.
-- [ ] - [ ] -------------------------------------------------------------
   CAPTURE RUN FINISHED
-- [ ] - [ ] -------------------------------------------------------------
 Read the save and copy messages above. Either step can fail.
 Review the summary before sharing it.
 Nothing was sent to a chatbot.
-- [ ] - [ ] -------------------------------------------------------------

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:nix-shell-env) (nix) npvg $ epr

Right? Thatโ€™s all so awesome except inside vim it looks like this:

1 # Written by the last completed walk. Edit freely: an edited router is never replaced.
  2 # One line per thing the AI should see: a file path, or a command after `! `.
  3 # The checked preview rides. The whole UNSANITIZED archive is the commented line beneath it.
  4 /Users/michaellevin/npvg/data/decant-preview.md
  5 # /Users/michaellevin/npvg/data/captures/walk-lzgidt2m/captures.md
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
~
adhocwalk.txt                                                                                                   1,1            All
init.lua loaded successfully!                                                                                             

And the text-portion should look more like this. You use your better judgment to make it even better, but this is my thoughts:

# ATTENTION HUMAN: Edit Prompt Router (epr) to include things in context.
# Welcome to vim (NeoVim). This file was written by the last walk. 

# Press `j` and `k` on your keyobard to move the cursor down and up. 
# Type `Esc`, `:q` to quit or `Esc`, `:q!` to force quit without save.

# The 1st line below is a table of contents for AI. Keep it in.
# The 2nd line below is potnetially huge and full of credentials so
# keep it edited out, but let the AI guide you after you type `cpr`.

# --- BEGIN LISTING FILES OR `! ` COMMANDS ---

introduction.md
/Users/michaellevin/npvg/data/decant-preview.md
# /Users/michaellevin/npvg/data/captures/walk-lzgidt2m/captures.md

Of course weโ€™ll have to make introduction.md too, ha ha!

All this making sense? I can haz training wheels?

Note: Wow, now thatโ€™s a chunk of thought-headlines!

Thought for 11m 18s

Reviewing an applied code patch and its context.

Tracing a rendering artifact in the pasted transcript.

Decoding the dash-prefix transform across both transcripts.

Assessing which files are missing from this turn's context.

Parsing the router header draft and its file listing convention.

Working out where the new introduction file should live.

Updating the guard logic to permit the new intro line.

Deciding where the new introduction file should reside.

Rewriting the closing narration to set clear next steps.

Balancing spoken clarity against exact command syntax.

Placing a volume reminder relative to the voice-ready message.

Deciding whether the hint deserves its own commit.

Drafting the vim header comment with accurate keybindings.

Finalizing the header text and file listing entries.

Extracting a helper to check router path existence.

Weighing how to plumb the first-write flag through calls.

Comparing plumbing effort against a self-contained marker approach.

Mapping exact anchor lines for each plumbing edit.

Checking for safe search-and-replace boundaries around each edit.

Resolving duplicate anchor lines with longer context blocks.

Considering a process-cached marker instead of full plumbing.

Drafting the module-level cache and header comment together.

Structuring header wording to match each preview scenario.

Locating the exact function boundaries for the patch.

Splitting the patch into smaller, safer anchored edits.

Isolating a unique anchor for the file-check guard block.

Excluding the intro line from the ownership guard check.

Checking comment lines against the parser's prose guard.

Drafting the full header text with separator and instructions.

Trimming the header text for brevity and clarity.

Drafting the introduction file's explanatory content.

Rewriting the closing narration for the successful walk.

Wording the closing narration around clipboard reliability.

Adding a volume hint after the voice disclosure line.

Auditing which files the next-turn context still omits.

Designing a throwaway probe to test the router logic.

Verifying import paths and file existence before running the probe.

Working around one-liner limits to test the guard's refusal.

Splitting the refusal test into two separate shell probes.

Checking probe text for unintended shell or regex triggers.

Drafting the vim lesson text as module-level constants.

Assembling the file listing legend and separator text.

Branching the header legend by preview presence.

Writing the vim lesson comments and cursor keybinding hints.

Refining the ownership guard to exclude the intro path.

Updating the docstring paragraph to match the new guard.

Drafting the new introduction file's opening sections.

Rewriting the closing narration's next-step instructions.

Scoping the next turn's file list and edits.

Predicting exact line and comment counts for each probe.

Simulating the before-and-after error states for each probe.

Planning next-turn edits and answering the context question.

Structuring the reply around context rules and receipts.

Extracting exact search blocks for each patch edit.

Verifying variable construction and unchanged write calls.

Drafting the volume hint comment for the disclosure branch.

Drafting next-turn instructions for the two remaining file edits.

Verifying the neovim edit count and gate command.

Fitting each comment line within the terminal width limit.

Trimming header lines to fit the terminal width.

Counting characters to keep each header line within width limits.

Assembling the vim lesson and header constants precisely.

Assembling the writer's legend and listing logic together.

Ordering guard placement against the write-cache logic.

Drafting a shell probe to verify the guard's behavior.

Writing a probe to test the writer's two output modes.

Drafting the introduction file's content and next-step guidance.

Counting content lines to predict the file's exact line total.

Assembling exact search blocks for the code patch.

Verifying patch block ordering and edit independence.

Fable 5.1 Extra: Context verified for every car below: scripts/mother_cat.py is here, the four receipts landed, and the writer, the guard, the closing narration and the disclosure line are all in that one file. Two cars from my last caboose cannot ride this turn, and that is my miss, not yours: I wrote โ€œinit.lua, flake.nix and the padโ€™s index.html are already there,โ€ and they were there only in the 40K-foot block you comment out on turn two. The rule Iโ€™ll hold from now on: NEXT CONTEXT names every file a next-turn car will patch, in full, even if it rode this compile. You never have to re-include anything on my behalf; if a file is missing, the list was wrong. Editing out the 40K-foot view on turn two costs nothing for understanding, since this discussion still carries it, but a patch needs the raw source in this payload, so index.html and flake.nix are listed explicitly below.

What the receipts say

The init.lua car closed in both lanes. Your hand-run BEFORE read tw=80 spell=true rnu=true; this compileโ€™s AFTER reads tw=0 spell=false rnu=false. And the Macโ€™s second epr screen, on v2.59, shows the gutter as 1 2 3 4 5, the cursor at 1,1, no mode line, and no squiggles: the same patch witnessed on a second machine. The two census lines read 0 and 0 as predicted: the pad does not teach the three keys, the flake pins no editor. The rg locator put the header at line 375 and the goodbye at 1038.

One correction to my own reading of your first screenshot. The -- [ ] - [ ] -- that now prefixes every --- Stop 1/3 and every rule of dashes in your pasted transcript is the transport rewriting a line-leading --; the file prints --- Stop 1/3. So last turnโ€™s mode line was -- VISUAL LINE -- with the brackets added in transit: the word was real, the junk was not. Nothing in this turnโ€™s patches quotes such a line.

What lands, and the one ruling reversed

The header is the training wheels, and it comes off by itself. _write_walk_router gains first: None decides by absence and caches the answer for the process, because the rider writes the router twice per ride (after ARCHIVE STATUS, again after DECANT) and the second write must not find the file its own first write made; True or False forces it, which is what the probe does. A router that did not exist before the ride gets the full lesson. Every later ride writes a three-line header with just the keys. A human who edits the file keeps it, wheels and all; a human who deletes it gets the lesson back next walk.

introduction.md rides first. A tracked file at the repo root, named in the router by absolute path, gated on existence so an older checkout writes a router without it. It tells the chatbot who compiled this, what just happened, what the person can do next, and which words they already know. The guard learns to tolerate that one line and still refuses anything else a human typed.

The legend names files, not ordinals. Your draft said โ€œthe 1st line, the 2nd line,โ€ and this repo already convicted hand-counted ordinals once (GUIDANCE COUNTS ARE HAND-TYPED). The legend says introduction.md, decant-preview.md, captures.md, so it stays true when a line is absent.

The closing narration now sets expectations. The 2026-09-15 ruling said the goodbye adds no command and no lesson; you reversed that today, in writing, with three numbered wants. The closing now says what was captured and where, that the next word is epr, what opens, and how to leave. It still refuses to claim the clipboard: decanted means the checked handoff was attempted and the preview file written, and copy_to_clipboard swallows its own failures, so the sentence points at the lines above rather than asserting a copy.

The volume line. โ€œVoice ready.โ€ is printed by imports/voice_synthesis.py, which is not here. The first sentence Piper speaks is the disclosure in _narrate, immediately after that download, so the hint prints right after it: โ€œIf you did not hear that, turn your volume up.โ€ Conditional on the ear, so it claims nothing.

:Tutor waits for the pin. It is a Neovim command, the flake pins no editor, and a strangerโ€™s Mac may fall through epr to Appleโ€™s vim. The line joins ROUTER_TAIL in the same fence as the neovim pin, next turn, with index.html teaching j, k, q inside less beside it. boot_menu.py is untouched: naming vim on the epr line is unasked, so it rides as a TODO below.

Here is the first router as the Mac will write it, every line under 79 columns because wrap is off and a hidden tail is a lie:

# ATTENTION HUMAN: this is the Prompt Router. epr edits it, cpr compiles it.
# Welcome to vim (Neovim). This file was written by the last walk.

# j moves the cursor down, k moves it up: the same two keys as in less.
# To leave: press Esc, then type  :q  and press Enter.
# Changed something by mistake? Esc, then  :q!  and Enter leaves it unsaved.
# Esc is always the way back to safety.
# Old joke: the best random-string generator is a beginner told to exit vi.

# Every line below that does not start with # is one thing the AI will read:
# a file path, or a shell command written after `! `.
# introduction.md tells the AI what this system is. Keep it.
# decant-preview.md is the checked summary of your walk. Keep it.
# captures.md is the raw UNSANITIZED archive; it is big and may hold secrets.
# Leave the # in front of it until an AI says it needs it, then read it first.

# --- BEGIN LISTING FILES OR `! ` COMMANDS ---
/Users/michaellevin/npvg/introduction.md
/Users/michaellevin/npvg/data/decant-preview.md
# /Users/michaellevin/npvg/data/captures/walk-lzgidt2m/captures.md

# Next: Esc, :q, Enter, then type  cpr  at the prompt.
# To add a line later: i starts typing, Esc stops, :wq saves and leaves.
# The next walk rewrites this file (shorter notes) unless you have edited it.

Yes, you can haz training wheels.

1. PROBES

The first line is the writer straddle, and it discriminates hard: BEFORE dies with TypeError: unexpected keyword argument 'first'; AFTER prints long 24 18 [...] and short 14 10 [...] (lines, comment lines, uncommented lines), the uncommented pair being this repoโ€™s introduction.md and the throwaway preview. The second line witnesses the guardโ€™s closed branch: BEFORE the same TypeError, AFTER ValueError: hand-edited router kept ... as the last line, and the ! ls line surviving (1). Lines three and four are the closing narration and the volume line, 0 to 1 each. Line five is the new file, โ€œNo such fileโ€ to 45. Lines six and seven are the CENSUS readings for next turnโ€™s two cars, 0 and 0, unchanged by anything here.

rm -rf /tmp/wheels; mkdir -p /tmp/wheels; PIPULATE_ADHOC_FILE=/tmp/wheels/adhoc.txt /home/mike/repos/pipulate/.venv/bin/python -c "import sys; sys.path.insert(0, '/home/mike/repos/pipulate/scripts'); import mother_cat as m; t = m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=True); L = open(t).read().splitlines(); print('long', len(L), sum(l.startswith('#') for l in L), [l for l in L if l and not l.startswith('#')]); t = m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=False); L = open(t).read().splitlines(); print('short', len(L), sum(l.startswith('#') for l in L), [l for l in L if l and not l.startswith('#')])"
printf '! ls\n' >> /tmp/wheels/adhocwalk.txt; PIPULATE_ADHOC_FILE=/tmp/wheels/adhoc.txt /home/mike/repos/pipulate/.venv/bin/python -c "import sys; sys.path.insert(0, '/home/mike/repos/pipulate/scripts'); import mother_cat as m; m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=False)" 2>&1 | tail -1; grep -c '^! ls' /tmp/wheels/adhocwalk.txt
grep -c 'Next, type epr' scripts/mother_cat.py
grep -c 'turn your volume up' scripts/mother_cat.py
wc -l introduction.md
grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
grep -c 'neovim' flake.nix

2. NEXT CONTEXT

The whole list, nothing assumed: the writer and its new file for the AFTER, and the two files next turnโ€™s cars patch.

# --- THE TRAINING WHEELS: the writer's AFTER, and the two files the next cars patch ---
scripts/mother_cat.py
introduction.md
remotes/honeybot/www/npvg.org/index.html
flake.nix
! rm -rf /tmp/wheels; mkdir -p /tmp/wheels; PIPULATE_ADHOC_FILE=/tmp/wheels/adhoc.txt /home/mike/repos/pipulate/.venv/bin/python -c "import sys; sys.path.insert(0, '/home/mike/repos/pipulate/scripts'); import mother_cat as m; t = m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=True); L = open(t).read().splitlines(); print('long', len(L), sum(l.startswith('#') for l in L), [l for l in L if l and not l.startswith('#')]); t = m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=False); L = open(t).read().splitlines(); print('short', len(L), sum(l.startswith('#') for l in L), [l for l in L if l and not l.startswith('#')])"
! printf '! ls\n' >> /tmp/wheels/adhocwalk.txt; PIPULATE_ADHOC_FILE=/tmp/wheels/adhoc.txt /home/mike/repos/pipulate/.venv/bin/python -c "import sys; sys.path.insert(0, '/home/mike/repos/pipulate/scripts'); import mother_cat as m; m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=False)" 2>&1 | tail -1; grep -c '^! ls' /tmp/wheels/adhocwalk.txt
! grep -c 'Next, type epr' scripts/mother_cat.py
! grep -c 'turn your volume up' scripts/mother_cat.py
! wc -l introduction.md
! grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
! grep -c 'neovim' flake.nix

3. PATCHES

Four cars. Each walk is a fresh process and each probe imports the file at call time, so no ignition is required anywhere in this train; the Mac gets it from the auto-update pull on its next nix develop (a blast is enough for that lane; PyPI was only ever for pip).

Car 1: the file the router names.

Target: introduction.md
[[[WRITE_FILE]]]
# Introduction: read this first

This text arrived by hand. A person compiled it on their own computer with
Pipulate, a small context compiler, and pasted it into this chat. Nothing in
it was sent automatically, and nothing you write here goes back to their
computer. The person installed the workshop today and finished its guided
introduction. They may never have used a terminal editor before, and their
home folder appears in the file paths of this payload: treat those paths as
private and do not repeat them anywhere.

## What Pipulate is

A workshop that runs on the person's own machine and is pinned with Nix, so
it behaves the same on macOS, Linux, and Windows under WSL. Its job here is
narrow: put evidence in front of you as plain text, in a fixed order, with a
record of how it was made, so that your answers can cite what you were shown
instead of guessing.

## What just happened

The guided introduction opened three public pages at npvg.org, one at a time,
in a visible browser. At each page the person typed CAPTURE in their
terminal, and the workshop saved what the server sent, what the browser
built, and the requests in between. Those raw captures stay on their disk.
A checked, trimmed summary of them, decant-preview.md, follows this file.

## What the person can do next

They edit a plain text file called the prompt router by typing epr. Every
line in it that does not start with # is one thing you will see the next
time they compile: a file path, or a shell command written after `! ` whose
output rides along. They compile it by typing cpr and paste the result here
again. The full archive, captures.md, is named in that file but commented
out, because it is unreviewed and may hold private details.

## How to help

Answer from the sections that follow this file, and say which section
supports each claim. If the summary does not carry what a question needs,
say so plainly and name where it would be found: a lens in the full archive,
a fresh walk, or a router line. When you suggest adding something to the
router, give the exact line to type, and say whether the person should read
that file themselves before compiling it. Use only the words they have
already met at the terminal (menu, walk, epr, cpr) and in the editor (j, k,
Esc, :q, :q!, :wq). Do not ask them to install anything.
[[[END_WRITE_FILE]]]

Car 2: the writer, the guard, and the wheels that come off by themselves. Five blocks, one commit story.

Target: scripts/mother_cat.py
[[[SEARCH]]]
def _router_line(path, what):
[[[DIVIDER]]]
# THE TRAINING WHEELS (2026-09-20, read off the first Mac epr screen). `epr`
# is a newcomer's first vim, and the router is the one place the lesson can
# sit where their eyes already are, so the first router a machine ever
# writes carries it. The keys are the bridge from less: j and k are the same
# two keys, and q grows a colon. Every lesson line is a comment, so the
# compiler's parser and the guard below both skip it, and every line stays
# under 79 columns because `wrap` is off in the router buffer and a hidden
# tail is a lie. The legend names files, never ordinals: a hand-counted
# "2nd line" goes stale the moment a line is absent.
INTRO_PATH = REPO_ROOT / "introduction.md"
ROUTER_LESSON = (
    "# ATTENTION HUMAN: this is the Prompt Router. epr edits it, cpr compiles it.\n"
    "# Welcome to vim (Neovim). This file was written by the last walk.\n"
    "\n"
    "# j moves the cursor down, k moves it up: the same two keys as in less.\n"
    "# To leave: press Esc, then type  :q  and press Enter.\n"
    "# Changed something by mistake? Esc, then  :q!  and Enter leaves it unsaved.\n"
    "# Esc is always the way back to safety.\n"
    "# Old joke: the best random-string generator is a beginner told to exit vi.\n"
    "\n"
    "# Every line below that does not start with # is one thing the AI will read:\n"
    "# a file path, or a shell command written after `! `.\n"
)
ROUTER_KEYS = (
    "# Prompt Router: epr edits it, cpr compiles it. Written by the last walk.\n"
    "# Keys: j down, k up; Esc then :q quits, :q! discards, :wq saves and quits.\n"
    "# One line per thing the AI reads: a file path, or a command after `! `.\n"
)
ROUTER_TAIL = (
    "\n"
    "# Next: Esc, :q, Enter, then type  cpr  at the prompt.\n"
    "# To add a line later: i starts typing, Esc stops, :wq saves and leaves.\n"
    "# The next walk rewrites this file (shorter notes) unless you have edited it.\n"
)
# THE WHEELS COME OFF BY THEMSELVES. "First" means the router did not exist
# before this ride. The rider writes it twice per ride (after ARCHIVE STATUS,
# then after DECANT), so the absence test runs once per process and its
# answer is cached here; the second write must not find the file its own
# first write made. A later walk finds the file, writes ROUTER_KEYS, and the
# wheels are off. A human who edits the router keeps it (the guard in the
# writer); one who deletes it gets the lesson back on the next walk.
_ROUTER_FIRST = {"first": None}

def _router_line(path, what):
[[[REPLACE]]]

Target: scripts/mother_cat.py
[[[SEARCH]]]
def _write_walk_router(archive_path, preview_path=None):
[[[DIVIDER]]]
def _write_walk_router(archive_path, preview_path=None, first=None):
[[[REPLACE]]]

Target: scripts/mother_cat.py
[[[SEARCH]]]
    THE ROUTER IS THE NEWCOMER'S FILE (2026-09-16): `epr` opens it, so it is
    replaced ONLY while it is a file this writer could have produced -- one
    uncommented absolute path to an archive or a preview and nothing else. Any
    other uncommented line is a human's; the new paths are printed for them
    and nothing is touched. With a preview, the preview is the line that rides
    and the archive is a commented line beneath it; without one, the archive
    rides alone, labelled UNSANITIZED.
[[[DIVIDER]]]
    THE ROUTER IS THE NEWCOMER'S FILE (2026-09-16): `epr` opens it, so it is
    replaced ONLY while it is a file this writer could have produced -- the
    introduction line at most, plus one uncommented absolute path to an
    archive or a preview, and nothing else. Any other uncommented line is a
    human's; the new paths are printed for them and nothing is touched. With
    a preview, the preview is the line that rides and the archive is a
    commented line beneath it; without one, the archive rides alone,
    labelled UNSANITIZED. THE FIRST ROUTER CARRIES THE VIM LESSON
    (2026-09-20): `first` is None to decide by absence, cached per process in
    _ROUTER_FIRST, or True/False to force it, as the probe does.
[[[REPLACE]]]

Target: scripts/mother_cat.py
[[[SEARCH]]]
    # REPLACE ONLY A FILE THIS WRITER COULD HAVE WRITTEN: one uncommented
    # absolute path to an archive or a preview, and nothing else. Any other
    # uncommented line is a human's, so the paths are printed for them instead.
    if target.is_file():
        owned = [line.strip() for line in target.read_text(encoding="utf-8").splitlines()
                 if line.strip() and not line.lstrip().startswith("#")]
        if owned and (len(owned) != 1 or not owned[0].startswith("/")
                      or not owned[0].endswith(("captures.md", "decant-preview.md"))):
[[[DIVIDER]]]
    # REPLACE ONLY A FILE THIS WRITER COULD HAVE WRITTEN: the introduction line
    # at most, plus one uncommented absolute path to an archive or a preview,
    # and nothing else. Any other uncommented line is a human's, so the paths
    # are printed for them instead.
    if target.is_file():
        owned = [line.strip() for line in target.read_text(encoding="utf-8").splitlines()
                 if line.strip() and not line.lstrip().startswith("#")]
        routed = [line for line in owned if line != str(INTRO_PATH)]
        if owned and (len(routed) != 1 or not routed[0].startswith("/")
                      or not routed[0].endswith(("captures.md", "decant-preview.md"))):
[[[REPLACE]]]

Target: scripts/mother_cat.py
[[[SEARCH]]]
    head = (
        "# Written by the last completed walk. Edit freely: an edited router is never replaced.\n"
        "# One line per thing the AI should see: a file path, or a command after `! `.\n"
    )
    if preview:
        body = (
            "# The checked preview rides. The whole UNSANITIZED archive is the commented line beneath it.\n"
            f"{preview}\n"
            f"# {source}\n"
        )
    else:
        body = (
            "# No checked preview was saved, so this is the whole UNSANITIZED archive. Review before compiling.\n"
            f"{source}\n"
        )
[[[DIVIDER]]]
    if first is None:
        if _ROUTER_FIRST["first"] is None:
            _ROUTER_FIRST["first"] = not target.is_file()
        first = _ROUTER_FIRST["first"]
    # Gated on existence: an older checkout without introduction.md writes a
    # router that names only what is on disk, and the compiler skips nothing.
    intro = _router_line(INTRO_PATH, "introduction") if INTRO_PATH.is_file() else None
    head = ROUTER_LESSON if first else ROUTER_KEYS
    legend = []
    if intro:
        legend.append("# introduction.md tells the AI what this system is. Keep it.")
    if preview:
        legend += [
            "# decant-preview.md is the checked summary of your walk. Keep it.",
            "# captures.md is the raw UNSANITIZED archive; it is big and may hold secrets.",
            "# Leave the # in front of it until an AI says it needs it, then read it first.",
        ]
        listing = [line for line in (intro, preview, f"# {source}") if line]
    else:
        legend.append(
            "# No checked preview was saved, so this is the whole UNSANITIZED archive. Review before compiling."
        )
        listing = [line for line in (intro, source) if line]
    body = "\n".join(legend + ["", "# --- BEGIN LISTING FILES OR `! ` COMMANDS ---"] + listing) + "\n"
    body += ROUTER_TAIL if first else "\n# The next walk rewrites this file unless you have edited it.\n"
[[[REPLACE]]]

Car 3: the walk says what comes next. Your three numbered wants, spoken and printed, without claiming the clipboard.

Target: scripts/mother_cat.py
[[[SEARCH]]]
            closing = (
                "The three-page walk is finished. "
                "Read the save and copy results in your terminal. "
                "Review any summary before sharing it. Goodbye."
                if decanted else
[[[DIVIDER]]]
            # THE GOODBYE SETS EXPECTATIONS (2026-09-20, operator's ruling,
            # reversing 2026-09-15's "no command, no lesson"): what was
            # captured and where, the next word, what it opens, how to leave.
            # It points at the save and copy lines rather than asserting a
            # copy, because copy_to_clipboard swallows its own failures.
            closing = (
                "The three-page walk is finished. Everything it captured is saved on this "
                "computer, and the lines above say whether the summary was saved and copied. "
                "Next, type epr: the letters e, p, r. That opens a text editor called vim, "
                "showing the list of files an AI will read. The keys j and k move the cursor. "
                "To leave, press Escape, then type colon q, then Enter. "
                "The notes at the top of that file say the same. Goodbye."
                if decanted else
[[[REPLACE]]]

Car 4: the volume line, right after the first spoken sentence.

Target: scripts/mother_cat.py
[[[SEARCH]]]
            if isinstance(result, dict) and not result.get("success"):
                print(
                    "  (voice disclosure failed: "
                    f"{result.get('error', 'unknown error')})"
                )
            disclosed = True
[[[DIVIDER]]]
            if isinstance(result, dict) and not result.get("success"):
                print(
                    "  (voice disclosure failed: "
                    f"{result.get('error', 'unknown error')})"
                )
            elif isinstance(result, dict):
                # THE VOLUME LINE (2026-09-20, operator's ask). "Voice ready."
                # is printed by the speaker during this first sentence, and
                # the ear is the only instrument that tells a muted output
                # from a working one. Conditional on the ear; claims nothing.
                print("  (If you did not hear that, turn your volume up.)")
            disclosed = True
[[[REPLACE]]]

4. PROMPT

Two cars this turn, one fence, because the header's :Tutor line depends on the editor pin and the header's "same two keys as in less" deserves a page that says so. The census lines read 0 and 0 last compile; they are this turn's BEFORE.

Car 1, remotes/honeybot/www/npvg.org/index.html: one sentence after the less line, "Inside less, j moves down, k moves up, and q quits." Fix "The first commands lets you" to "The first command lets you" in the same car. Ignition: ./nixops.sh, no rebuild; the door serves the page only to a non-curl, non-wget agent, so the served-body check uses -A Mozilla. AFTER: the grep -ciE probe reads 1.

Car 2, flake.nix commonPackages: neovim, one line, with a comment naming epr and :Tutor as the reason, so epr and VIMINIT's luafile are the same editor on Linux, macOS and WSL (today a Mac without nvim falls to /usr/bin/vim, where luafile is INFERRED to error at load). In the SAME fence, scripts/mother_cat.py ROUTER_TAIL gains "# Practice vim any time: type  :Tutor  and press Enter." before its last line. Gate before it rides: nix eval --raw .#devShells.aarch64-darwin.quiet.drvPath and the x86_64-linux twin both evaluate. Ignition: exit then nix develop. AFTER: grep -c 'neovim' flake.nix reads 1 plus however many times the comment names it (count the replace), and the writer probe's long count reads 25.

Read the Mac transcript pasted below as the operator-lane receipts for last turn's four cars: the first-router header as the new writer wrote it, the closing narration as printed and heard, the volume line after "Voice ready.", and whether the second walk on the same install wrote the three-line header (the wheels coming off).

TODOs to bank when foo_files.py next rides, one line each: NEXT CONTEXT IS THE WHOLE LIST (a next-turn car's file is named in full, never "already there"; convicted 2026-09-20 when two cars lost their raw source to the turn-two edit-out); claim introduction.md in chapter VIII-b under HAND OFF; boot_menu.py's epr line may name the editor; init.lua's "init.lua loaded successfully!" print is a newcomer's first message line and is unasked.

5. EXTERNAL DELIVERABLES

None this turn outside the repo.

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:

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 $ rm -rf /tmp/wheels; mkdir -p /tmp/wheels; PIPULATE_ADHOC_FILE=/tmp/wheels/adhoc.txt /home/mike/repos/pipulate/.venv/bin/python -c "import sys; sys.path.insert(0, '/home/mike/repos/pipulate/scripts'); import mother_cat as m; t = m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=True); L = open(t).read().splitlines(); print('long', len(L), sum(l.startswith('#') for l in L), [l for l in L if l and not l.startswith('#')]); t = m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=False); L = open(t).read().splitlines(); print('short', len(L), sum(l.startswith('#') for l in L), [l for l in L if l and not l.startswith('#')])"
printf '! ls\n' >> /tmp/wheels/adhocwalk.txt; PIPULATE_ADHOC_FILE=/tmp/wheels/adhoc.txt /home/mike/repos/pipulate/.venv/bin/python -c "import sys; sys.path.insert(0, '/home/mike/repos/pipulate/scripts'); import mother_cat as m; m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=False)" 2>&1 | tail -1; grep -c '^! ls' /tmp/wheels/adhocwalk.txt
grep -c 'Next, type epr' scripts/mother_cat.py
grep -c 'turn your volume up' scripts/mother_cat.py
wc -l introduction.md
grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
grep -c 'neovim' flake.nix
Traceback (most recent call last):
  File "<string>", line 1, in <module>
TypeError: _write_walk_router() got an unexpected keyword argument 'first'
TypeError: _write_walk_router() got an unexpected keyword argument 'first'
1
0
0
wc: introduction.md: No such file or directory
0
0
(nix) pipulate $ 

2: Context:

# 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    _   _   _               ____ _   _  ___  ____  _   
#     / \   __| | | | | | ___   ___   / ___| | | |/ _ \|  _ \| |  This is *exactly like riding a bike* and we need some training wheels here and a designed "letting go" moment. Let's take good care of the New-B's. ***WE NEED TO HAVE A LOT OF EMPATHY WITH THEM***.
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | |   | |_| | | | | |_) | |  That looks like a very small context. Are you sure that's enough? To I have to be explicit about re-including the ones you know I'm going to be editing out on turn 2 from the 40K-FT VIEW?
# ahc ___ \ (_| | |  _  | (_) | (__  | |___|  _  | |_| |  __/|_|  OMG, this is it. This is the magic. This is where we put the v in NPvg. And do we need something to make sure you LLMs know to make Context inclusive regardless of what appears like it will be included automatically? If you don't have it in the current context-window you can't edit it, so think ahead to the next turn when making context. Where should we strategically insert that? In apply.py itself (always included)?
#  /_/   \_\__,_| |_| |_|\___/ \___|  \____|_| |_|\___/|_|   (_)  

# 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)
# assets/installer/install.sh # <-- Pipulate.com installer real home in github/pipulate repo
# 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 (Edit-in selections from above and add new files immediately below)
# /home/mike/repos/grimoire/_posts/2026-09-20-flatnotes-trilium-parity-321-ledger-replayable-backups-nixos.md
# ~/repos/nixos/autognome.py                 #  <-- More rare to have to include, but the true "top" of the muscle memory stack for day-to-day purposes
# ~/repos/nixos/configuration.nix            #  <-- "Global" IaC context (most of you won't have)
# ~/repos/nixos/blogs.nix
# ~/repos/nixos/packages.nix                 #  <-- Full disclosure on pre-flake IaC available apps.
# ~/repos/nixos/services.nix                 #  <-- Running Linux system services.
# ~/repos/nixos/ai-acceleration.nix          #  <-- Paid a lot for your hardware? We've got you covered.
# ~/repos/nixos/hardware-configuration.nix   #  <-- Automatically generated by Nix. The ultimate in IaC transparency.
# ~/repos/nixos/flatnotes.nix                #  <-- One easy local way to edit all your markdown in a browser
# ~/repos/nixos/openclaw.nix                 #  <-- Available but usually disabled (because I don't want to be)
# ~/repos/nixos/scripts/backup-nix.sh        #  <-- Nix is already a `/nix/store/` "backup". This backs that up.
# ~/repos/nixos/scripts/backup-essential.py  #  <-- Reconstitute much of your system from your keychain
# ~/repos/nixos/scripts/backup-force.py      #  <-- The occasional "big sync" to get thing2 identical to thing1
# ~/repos/nixos/scripts/backup-home.py       #  <-- The fast, easy, frequent backup for 80/20-rule data safety
# ~/repos/nixos/scripts/backup-things.py     #  <-- rsync'ing from thing1 to thing2, can be time consuming.
# flake.nix

# Context 2
# /home/mike/repos/nixos/scripts/dedupe.py
# /home/mike/repos/nixos/configuration.nix
# /home/mike/repos/nixos/scripts/backup-home.py
# ! env -i /home/mike/repos/pipulate/.venv/bin/python -c "import sys, sqlite3; print(sys.version.split()[0], 'sqlite', sqlite3.sqlite_version); import PIL.Image; print('PIL', PIL.__version__)"
# ! f=$(mktemp /tmp/dedupe-gio.XXXXXX) && LD_LIBRARY_PATH= gio trash "$f"; echo "gio_exit=$? still_there=$([ -e "$f" ] && echo yes || echo no) gio=$(command -v gio)"
# ! bash -ic 'type dedupe' 2>&1 | tail -1
# ! /home/mike/repos/pipulate/.venv/bin/python -c "import os, shutil; from pathlib import Path; from PIL import Image; H = '/tmp/dedupe-smoke'; R = H + '/root'; shutil.rmtree(H, ignore_errors=True); os.makedirs(R + '/sub'); g = Image.linear_gradient('L').point(lambda v: v // 2 + 40); a = Image.merge('RGB', (g, g.rotate(90), g.rotate(180))).resize((64, 48)); b = Image.merge('RGB', (g, g.rotate(-90), g.rotate(180))).resize((64, 48)); a.save(R + '/a_orig.png'); shutil.copy(R + '/a_orig.png', R + '/a_copy.png'); b.save(R + '/sub/b.jpg', quality=95); b.save(R + '/sub/b_resaved.jpg', quality=70); Image.new('RGB', (64, 48), (200, 30, 30)).save(R + '/c_unique.png'); Path(R + '/blob1.bin').write_bytes(b'x' * 5000); shutil.copy(R + '/blob1.bin', R + '/blob2.bin'); [os.utime(R + p, (1e9, 1e9)) for p in ('/a_orig.png', '/sub/b.jpg', '/blob1.bin')]; print('fixture:', sorted(os.listdir(R)), sorted(os.listdir(R + '/sub')))"
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root
# ! find /tmp/dedupe-smoke/root -type f | sort; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --top

# Context 3
# /home/mike/repos/nixos/scripts/dedupe.py
# /home/mike/repos/nixos/configuration.nix
# ! ls -d /mnt/thing1/*/ ~/Pictures 2>&1 | head -40
# ! f=$(mktemp /mnt/thing1/dedupe-gio.XXXXXX) && LD_LIBRARY_PATH= gio trash "$f"; echo "gio_exit=$? still_there=$([ -e "$f" ] && echo yes || echo no)"; ls -d /mnt/thing1/.Trash-1000 2>&1
# ! /home/mike/repos/pipulate/.venv/bin/python -c "import os, shutil; from pathlib import Path; from PIL import Image; H = '/tmp/dedupe-smoke'; R = H + '/root'; shutil.rmtree(H, ignore_errors=True); os.makedirs(R + '/sub'); g = Image.linear_gradient('L').point(lambda v: v // 2 + 40); a = Image.merge('RGB', (g, g.rotate(90), g.rotate(180))).resize((64, 48)); b = Image.merge('RGB', (g, g.rotate(-90), g.rotate(180))).resize((64, 48)); a.save(R + '/a_orig.png'); shutil.copy(R + '/a_orig.png', R + '/a_copy.png'); b.save(R + '/sub/b.jpg', quality=95); b.save(R + '/sub/b_resaved.jpg', quality=70); Image.new('RGB', (64, 48), (200, 30, 30)).save(R + '/c_unique.png'); Path(R + '/blob1.bin').write_bytes(b'x' * 5000); shutil.copy(R + '/blob1.bin', R + '/blob2.bin'); [os.utime(R + p, (1e9, 1e9)) for p in ('/a_orig.png', '/sub/b.jpg', '/blob1.bin')]; print('fixture:', sorted(os.listdir(R)), sorted(os.listdir(R + '/sub')))"
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root
# ! find /tmp/dedupe-smoke/root -type f | sort; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --top
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --where img1:ffffffffffffffff:64x48:666

# Context 4
# /home/mike/repos/nixos/scripts/dedupe.py
# /home/mike/repos/nixos/configuration.nix
# ! /home/mike/repos/pipulate/.venv/bin/python -c "import os, shutil; from pathlib import Path; from PIL import Image; H = '/tmp/dedupe-smoke'; R = H + '/root'; shutil.rmtree(H, ignore_errors=True); os.makedirs(R + '/sub'); g = Image.linear_gradient('L').point(lambda v: v // 2 + 40); a = Image.merge('RGB', (g, g.rotate(90), g.rotate(180))).resize((64, 48)); b = Image.merge('RGB', (g, g.rotate(-90), g.rotate(180))).resize((64, 48)); a.save(R + '/a_orig.png'); shutil.copy(R + '/a_orig.png', R + '/a_copy.png'); b.save(R + '/sub/b.jpg', quality=95); b.save(R + '/sub/b_resaved.jpg', quality=70); Image.new('RGB', (64, 48), (200, 30, 30)).save(R + '/c_unique.png'); Path(R + '/blob1.bin').write_bytes(b'x' * 5000); shutil.copy(R + '/blob1.bin', R + '/blob2.bin'); [os.utime(R + p, (1e9, 1e9)) for p in ('/a_orig.png', '/sub/b.jpg', '/blob1.bin')]; print('fixture:', sorted(os.listdir(R)), sorted(os.listdir(R + '/sub')))"
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root
# ! find /tmp/dedupe-smoke/root -type f | sort; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --top
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --where img1:ffffffffffffffff:64x48:666
# ! LD_LIBRARY_PATH= script -qec "PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run" /dev/null | tr '\r' '\n' | grep -c "$(printf '\033')\[2K"; echo "script=$(command -v script)"

# Context 4
# /home/mike/repos/nixos/scripts/dedupe.py
# /home/mike/repos/nixos/configuration.nix
# ! /home/mike/repos/pipulate/.venv/bin/python -c "import os, shutil; from pathlib import Path; from PIL import Image; H = '/tmp/dedupe-smoke'; R = H + '/root'; shutil.rmtree(H, ignore_errors=True); os.makedirs(R + '/sub'); g = Image.linear_gradient('L').point(lambda v: v // 2 + 40); a = Image.merge('RGB', (g, g.rotate(90), g.rotate(180))).resize((64, 48)); b = Image.merge('RGB', (g, g.rotate(-90), g.rotate(180))).resize((64, 48)); a.save(R + '/a_orig.png'); shutil.copy(R + '/a_orig.png', R + '/a_copy.png'); b.save(R + '/sub/b.jpg', quality=95); b.save(R + '/sub/b_resaved.jpg', quality=70); Image.new('RGB', (64, 48), (200, 30, 30)).save(R + '/c_unique.png'); Path(R + '/blob1.bin').write_bytes(b'x' * 5000); shutil.copy(R + '/blob1.bin', R + '/blob2.bin'); [os.utime(R + p, (1e9, 1e9)) for p in ('/a_orig.png', '/sub/b.jpg', '/blob1.bin')]; print('fixture:', sorted(os.listdir(R)), sorted(os.listdir(R + '/sub')))"
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root --dry-run
# ! LD_LIBRARY_PATH= script -qec "PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root" /dev/null | tr '\r' '\n' > /tmp/dedupe-pty.txt; echo "redraws=$(grep -c "$(printf '\033')\[2K" /tmp/dedupe-pty.txt) trashed_lines=$(grep -c 'trashed /tmp' /tmp/dedupe-pty.txt)"; grep -o 'pass [0-9]*: .*' /tmp/dedupe-pty.txt
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py /tmp/dedupe-smoke/root
# ! find /tmp/dedupe-smoke/root -type f | sort; PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --top
# ! PIPULATE_DEDUPE_HOME=/tmp/dedupe-smoke/home /home/mike/repos/pipulate/.venv/bin/python /home/mike/repos/nixos/scripts/dedupe.py --where img1:ffffffffffffffff:64x48:666
# ! (cd /home/mike/repos/nixos/scripts && /home/mike/repos/pipulate/.venv/bin/python -c "import dedupe as d; R = '/mnt/thing1/Sort Big/New/'; f = lambda a, b: (lambda p, q: print(a, 'vs', b, 'max', max(abs(x - y) for x, y in zip(p, q)), 'cells_over', sum(abs(x - y) > d.CONFIRM_TOLERANCE for x, y in zip(p, q))))(d.thumb(R + a), d.thumb(R + b)); [f(a, b) for a, b in (('IMG_4029.PNG', 'IMG_4029-1.PNG'), ('IMG_3128.PNG', 'IMG_3129.PNG'), ('682278670.JPG', '682278670-1.JPG'), ('IMG_6507.JPG', 'IMG_6508.JPG'), ('IMG_9556.PNG', 'IMG_9557.PNG'), ('IMG_3282.PNG', 'IMG_3283.PNG'))]")

# Context 1
# release.py                                 # <-- The deploy process
# assets/installer/install.sh                # <-- Pipulate.com installer real home in github/pipulate repo
# ~/repos/Pipulate.com/_layouts/default.html
# ~/repos/Pipulate.com/install.md            # <-- Gets copied into place here by pipulate/release.py
# nixops.sh                                  # <-- You've heard of GitOPs? Well, this is NixOPs. 
# remotes/honeybot/hooks/post-receive        # <-- Ever hear of GitHub Pages? Or github.io? This is that.
# remotes/honeybot/nixos/configuration.nix   # <-- It's as if Pipulate had kids. Spy kids.
# remotes/honeybot/www/npvg.org/index.html   # <-- New "Pipulate" install site under operation stick bug

# Context 2
# --- THE NEWCOMER'S FIRST VIM: the router writer and the menu that names epr ---
# scripts/mother_cat.py
# scripts/boot_menu.py
# ! rg -n 'Written by the last completed walk|def _write_walk_router|Goodbye' scripts/mother_cat.py
# ! VIMINIT='luafile init.lua' nvim --headless adhocwalk.txt -c 'lua io.write(string.format("tw=%d spell=%s rnu=%s\n", vim.bo.textwidth, tostring(vim.wo.spell), tostring(vim.wo.relativenumber)))' -c 'qa!'
# ! grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
# ! grep -c 'neovim' flake.nix

# Context 3
# --- THE TRAINING WHEELS: the writer's AFTER, and the two files the next cars patch ---
scripts/mother_cat.py
introduction.md
remotes/honeybot/www/npvg.org/index.html
flake.nix
! rm -rf /tmp/wheels; mkdir -p /tmp/wheels; PIPULATE_ADHOC_FILE=/tmp/wheels/adhoc.txt /home/mike/repos/pipulate/.venv/bin/python -c "import sys; sys.path.insert(0, '/home/mike/repos/pipulate/scripts'); import mother_cat as m; t = m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=True); L = open(t).read().splitlines(); print('long', len(L), sum(l.startswith('#') for l in L), [l for l in L if l and not l.startswith('#')]); t = m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=False); L = open(t).read().splitlines(); print('short', len(L), sum(l.startswith('#') for l in L), [l for l in L if l and not l.startswith('#')])"
! printf '! ls\n' >> /tmp/wheels/adhocwalk.txt; PIPULATE_ADHOC_FILE=/tmp/wheels/adhoc.txt /home/mike/repos/pipulate/.venv/bin/python -c "import sys; sys.path.insert(0, '/home/mike/repos/pipulate/scripts'); import mother_cat as m; m._write_walk_router('/tmp/wheels/captures.md', preview_path='/tmp/wheels/decant-preview.md', first=False)" 2>&1 | tail -1; grep -c '^! ls' /tmp/wheels/adhocwalk.txt
! grep -c 'Next, type epr' scripts/mother_cat.py
! grep -c 'turn your volume up' scripts/mother_cat.py
! wc -l introduction.md
! grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
! grep -c 'neovim' flake.nix

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

You got my question there about somehow better-than-constitutionalize the โ€œeverything youโ€™re gonna needโ€ rule for next context?

3: Patches:

(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 $ patch
(nix) pipulate $ app
โœ… WHOLE-FILE WRITE: CREATED 'introduction.md'.
(nix) pipulate $ git add introduction.md
(nix) pipulate $ m
๐Ÿ“ Committing: chore: Update introduction.md with workshop guided introduction content
[main c5fce54a] chore: Update introduction.md with workshop guided introduction content
 1 file changed, 45 insertions(+)
 create mode 100644 introduction.md
(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'.
โœ… 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 20a33709..ffc070b2 100644
--- a/scripts/mother_cat.py
+++ b/scripts/mother_cat.py
@@ -326,6 +326,50 @@ def _bank_capture(archive, trail, index, stop, params, result):
     return problems
 
 
+# THE TRAINING WHEELS (2026-09-20, read off the first Mac epr screen). `epr`
+# is a newcomer's first vim, and the router is the one place the lesson can
+# sit where their eyes already are, so the first router a machine ever
+# writes carries it. The keys are the bridge from less: j and k are the same
+# two keys, and q grows a colon. Every lesson line is a comment, so the
+# compiler's parser and the guard below both skip it, and every line stays
+# under 79 columns because `wrap` is off in the router buffer and a hidden
+# tail is a lie. The legend names files, never ordinals: a hand-counted
+# "2nd line" goes stale the moment a line is absent.
+INTRO_PATH = REPO_ROOT / "introduction.md"
+ROUTER_LESSON = (
+    "# ATTENTION HUMAN: this is the Prompt Router. epr edits it, cpr compiles it.\n"
+    "# Welcome to vim (Neovim). This file was written by the last walk.\n"
+    "\n"
+    "# j moves the cursor down, k moves it up: the same two keys as in less.\n"
+    "# To leave: press Esc, then type  :q  and press Enter.\n"
+    "# Changed something by mistake? Esc, then  :q!  and Enter leaves it unsaved.\n"
+    "# Esc is always the way back to safety.\n"
+    "# Old joke: the best random-string generator is a beginner told to exit vi.\n"
+    "\n"
+    "# Every line below that does not start with # is one thing the AI will read:\n"
+    "# a file path, or a shell command written after `! `.\n"
+)
+ROUTER_KEYS = (
+    "# Prompt Router: epr edits it, cpr compiles it. Written by the last walk.\n"
+    "# Keys: j down, k up; Esc then :q quits, :q! discards, :wq saves and quits.\n"
+    "# One line per thing the AI reads: a file path, or a command after `! `.\n"
+)
+ROUTER_TAIL = (
+    "\n"
+    "# Next: Esc, :q, Enter, then type  cpr  at the prompt.\n"
+    "# To add a line later: i starts typing, Esc stops, :wq saves and leaves.\n"
+    "# The next walk rewrites this file (shorter notes) unless you have edited it.\n"
+)
+# THE WHEELS COME OFF BY THEMSELVES. "First" means the router did not exist
+# before this ride. The rider writes it twice per ride (after ARCHIVE STATUS,
+# then after DECANT), so the absence test runs once per process and its
+# answer is cached here; the second write must not find the file its own
+# first write made. A later walk finds the file, writes ROUTER_KEYS, and the
+# wheels are off. A human who edits the router keeps it (the guard in the
+# writer); one who deletes it gets the lesson back on the next walk.
+_ROUTER_FIRST = {"first": None}
+
+
 def _router_line(path, what):
     """One absolute path as one router line, or refuse: the grammar has no escapes."""
     text = str(path)
@@ -335,16 +379,19 @@ def _router_line(path, what):
     return text
 
 
-def _write_walk_router(archive_path, preview_path=None):
+def _write_walk_router(archive_path, preview_path=None, first=None):
     """Replace one local selection; never read, disclose or compile its evidence.
 
     THE ROUTER IS THE NEWCOMER'S FILE (2026-09-16): `epr` opens it, so it is
-    replaced ONLY while it is a file this writer could have produced -- one
-    uncommented absolute path to an archive or a preview and nothing else. Any
-    other uncommented line is a human's; the new paths are printed for them
-    and nothing is touched. With a preview, the preview is the line that rides
-    and the archive is a commented line beneath it; without one, the archive
-    rides alone, labelled UNSANITIZED.
+    replaced ONLY while it is a file this writer could have produced -- the
+    introduction line at most, plus one uncommented absolute path to an
+    archive or a preview, and nothing else. Any other uncommented line is a
+    human's; the new paths are printed for them and nothing is touched. With
+    a preview, the preview is the line that rides and the archive is a
+    commented line beneath it; without one, the archive rides alone,
+    labelled UNSANITIZED. THE FIRST ROUTER CARRIES THE VIM LESSON
+    (2026-09-20): `first` is None to decide by absence, cached per process in
+    _ROUTER_FIRST, or True/False to force it, as the probe does.
     """
     selected = os.environ.get("PIPULATE_ADHOC_FILE", str(REPO_ROOT / "adhoc.txt"))
     if not selected.strip():
@@ -359,33 +406,45 @@ def _write_walk_router(archive_path, preview_path=None):
         raise ValueError("walk router aliases the human router or is a symlink")
     source = _router_line(archive_path, "capture")
     preview = None if preview_path is None else _router_line(preview_path, "preview")
-    # REPLACE ONLY A FILE THIS WRITER COULD HAVE WRITTEN: one uncommented
-    # absolute path to an archive or a preview, and nothing else. Any other
-    # uncommented line is a human's, so the paths are printed for them instead.
+    # REPLACE ONLY A FILE THIS WRITER COULD HAVE WRITTEN: the introduction line
+    # at most, plus one uncommented absolute path to an archive or a preview,
+    # and nothing else. Any other uncommented line is a human's, so the paths
+    # are printed for them instead.
     if target.is_file():
         owned = [line.strip() for line in target.read_text(encoding="utf-8").splitlines()
                  if line.strip() and not line.lstrip().startswith("#")]
-        if owned and (len(owned) != 1 or not owned[0].startswith("/")
-                      or not owned[0].endswith(("captures.md", "decant-preview.md"))):
+        routed = [line for line in owned if line != str(INTRO_PATH)]
+        if owned and (len(routed) != 1 or not routed[0].startswith("/")
+                      or not routed[0].endswith(("captures.md", "decant-preview.md"))):
             raise ValueError(
                 "hand-edited router kept, nothing written; add the line(s) yourself: "
                 + " ".join(line for line in (preview, source) if line)
                 + " (or delete the file and the next walk rewrites it)")
-    head = (
-        "# Written by the last completed walk. Edit freely: an edited router is never replaced.\n"
-        "# One line per thing the AI should see: a file path, or a command after `! `.\n"
-    )
+    if first is None:
+        if _ROUTER_FIRST["first"] is None:
+            _ROUTER_FIRST["first"] = not target.is_file()
+        first = _ROUTER_FIRST["first"]
+    # Gated on existence: an older checkout without introduction.md writes a
+    # router that names only what is on disk, and the compiler skips nothing.
+    intro = _router_line(INTRO_PATH, "introduction") if INTRO_PATH.is_file() else None
+    head = ROUTER_LESSON if first else ROUTER_KEYS
+    legend = []
+    if intro:
+        legend.append("# introduction.md tells the AI what this system is. Keep it.")
     if preview:
-        body = (
-            "# The checked preview rides. The whole UNSANITIZED archive is the commented line beneath it.\n"
-            f"{preview}\n"
-            f"# {source}\n"
-        )
+        legend += [
+            "# decant-preview.md is the checked summary of your walk. Keep it.",
+            "# captures.md is the raw UNSANITIZED archive; it is big and may hold secrets.",
+            "# Leave the # in front of it until an AI says it needs it, then read it first.",
+        ]
+        listing = [line for line in (intro, preview, f"# {source}") if line]
     else:
-        body = (
-            "# No checked preview was saved, so this is the whole UNSANITIZED archive. Review before compiling.\n"
-            f"{source}\n"
+        legend.append(
+            "# No checked preview was saved, so this is the whole UNSANITIZED archive. Review before compiling."
         )
+        listing = [line for line in (intro, source) if line]
+    body = "\n".join(legend + ["", "# --- BEGIN LISTING FILES OR `! ` COMMANDS ---"] + listing) + "\n"
+    body += ROUTER_TAIL if first else "\n# The next walk rewrites this file unless you have edited it.\n"
     temp = None
     try:
         with tempfile.NamedTemporaryFile(
(nix) pipulate $ m
๐Ÿ“ Committing: chore: Introduce router documentation for new users
[main 4c33e0f6] chore: Introduce router documentation for new users
 1 file changed, 83 insertions(+), 24 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
โœ… 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 ffc070b2..f6dc450f 100644
--- a/scripts/mother_cat.py
+++ b/scripts/mother_cat.py
@@ -1091,10 +1091,18 @@ async def _ride_steps(trail_path, archive, dry_narrate=False, exports_path=None,
             print("   Review the summary before sharing it. You choose what to send.")
         # The archive file line was printed when its status was banked.
         if intro:
+            # THE GOODBYE SETS EXPECTATIONS (2026-09-20, operator's ruling,
+            # reversing 2026-09-15's "no command, no lesson"): what was
+            # captured and where, the next word, what it opens, how to leave.
+            # It points at the save and copy lines rather than asserting a
+            # copy, because copy_to_clipboard swallows its own failures.
             closing = (
-                "The three-page walk is finished. "
-                "Read the save and copy results in your terminal. "
-                "Review any summary before sharing it. Goodbye."
+                "The three-page walk is finished. Everything it captured is saved on this "
+                "computer, and the lines above say whether the summary was saved and copied. "
+                "Next, type epr: the letters e, p, r. That opens a text editor called vim, "
+                "showing the list of files an AI will read. The keys j and k move the cursor. "
+                "To leave, press Escape, then type colon q, then Enter. "
+                "The notes at the top of that file say the same. Goodbye."
                 if decanted else
                 "The capture run is finished, but the summary was withheld. "
                 "Read the results in your terminal. Goodbye."
(nix) pipulate $ m
๐Ÿ“ Committing: chore: Refine goodbye message in mother_cat.py
[main 1f82ac2a] chore: Refine goodbye message in mother_cat.py
 1 file changed, 11 insertions(+), 3 deletions(-)
(nix) pipulate $ patch
(nix) pipulate $ app
โœ… 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 f6dc450f..325b088c 100644
--- a/scripts/mother_cat.py
+++ b/scripts/mother_cat.py
@@ -89,6 +89,12 @@ def _narrate(text, disclosed):
                     "  (voice disclosure failed: "
                     f"{result.get('error', 'unknown error')})"
                 )
+            elif isinstance(result, dict):
+                # THE VOLUME LINE (2026-09-20, operator's ask). "Voice ready."
+                # is printed by the speaker during this first sentence, and
+                # the ear is the only instrument that tells a muted output
+                # from a working one. Conditional on the ear; claims nothing.
+                print("  (If you did not hear that, turn your volume up.)")
             disclosed = True
 
         result = chip_voice_system.speak_text(text)
(nix) pipulate $ m
๐Ÿ“ Committing: chore: Handle voice disclosure failure messages
[main aca2396e] chore: Handle voice disclosure failure messages
 1 file changed, 6 insertions(+)
(nix) pipulate $ git push
Enumerating objects: 18, done.
Counting objects: 100% (18/18), done.
Delta compression using up to 48 threads
Compressing objects: 100% (15/15), done.
Writing objects: 100% (15/15), 5.01 KiB | 2.51 MiB/s, done.
Total 15 (delta 10), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (10/10), completed with 3 local objects.
To github.com:pipulate/pipulate.git
   d639025b..aca2396e  main -> main
(nix) pipulate $ 

When people say they donโ€™t look at the code anymore or they just set a goal and let something run overnight, I just donโ€™t see it. I couldnโ€™t do the things Iโ€™m doing if I werenโ€™t breathing down the neck of the LLM like I just did last turn. The nuance and subtlety here is that of a craftsperson measuring every chisel-strike least they chop off the sculptures arm. How could โ€œwhat done looks likeโ€ be even done in this case without feeling it out as you go.

I donโ€™t think those people are creating something like I am. I could be wrong, but if itโ€™s something new under the Noosphere, itโ€™s not going to happen by puling a ripcord and shouting GO! Or am I just talking out my ass here?

Hand-Crafted Nuance Versus the Overnight Ripcord

4: Prompt:

Two cars this turn, one fence, because the headerโ€™s :Tutor line depends on the editor pin and the headerโ€™s โ€œsame two keys as in lessโ€ deserves a page that says so. The census lines read 0 and 0 last compile; they are this turnโ€™s BEFORE.

Car 1, remotes/honeybot/www/npvg.org/index.html: one sentence after the less line, โ€œInside less, j moves down, k moves up, and q quits.โ€ Fix โ€œThe first commands lets youโ€ to โ€œThe first command lets youโ€ in the same car. Ignition: ./nixops.sh, no rebuild; the door serves the page only to a non-curl, non-wget agent, so the served-body check uses -A Mozilla. AFTER: the grep -ciE probe reads 1.

Car 2, flake.nix commonPackages: neovim, one line, with a comment naming epr and :Tutor as the reason, so epr and VIMINITโ€™s luafile are the same editor on Linux, macOS and WSL (today a Mac without nvim falls to /usr/bin/vim, where luafile is INFERRED to error at load). In the SAME fence, scripts/mother_cat.py ROUTER_TAIL gains โ€œ# Practice vim any time: type :Tutor and press Enter.โ€ before its last line. Gate before it rides: nix eval โ€“raw .#devShells.aarch64-darwin.quiet.drvPath and the x86_64-linux twin both evaluate. Ignition: exit then nix develop. AFTER: grep -c โ€˜neovimโ€™ flake.nix reads 1 plus however many times the comment names it (count the replace), and the writer probeโ€™s long count reads 25.

Read the Mac transcript pasted below as the operator-lane receipts for last turnโ€™s four cars: the first-router header as the new writer wrote it, the closing narration as printed and heard, the volume line after โ€œVoice ready.โ€, and whether the second walk on the same install wrote the three-line header (the wheels coming off).

TODOs to bank when foo_files.py next rides, one line each: NEXT CONTEXT IS THE WHOLE LIST (a next-turn carโ€™s file is named in full, never โ€œalready thereโ€; convicted 2026-09-20 when two cars lost their raw source to the turn-two edit-out); claim introduction.md in chapter VIII-b under HAND OFF; boot_menu.pyโ€™s epr line may name the editor; init.luaโ€™s โ€œinit.lua loaded successfully!โ€ print is a newcomerโ€™s first message line and is unasked.

5: Deliverables: An example of real hand-craftsmanship in tech, even in the age of agentic loops. Or not. You tell me.

Oh, I should do another Mac turn here. Ugh, okay. First a release.

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

nothing to commit, working tree clean
(nix) pipulate $ vim __init__.py 
(nix) pipulate $ release
โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ๐Ÿฐ 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           |__>-\_>_>  \____/                     ๐Ÿฅ•๐Ÿฅ•๐Ÿฅ•                                    โ”‚
โ”‚                                                                                                                             โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
๐Ÿ“‹ Current version: 2.60
โœ… Updated pyproject.toml (version and description)
[main dda6b9d1] chore: Update version number in __init__.py and pyproject.toml
โœ… Pushed ba13c8cf..dda6b9d1  main -> main
Successfully built pipulate-2.60.tar.gz and pipulate-2.60-py3-none-any.whl
๐ŸŽ‰ Published 2.60 -> https://pypi.org/project/pipulate/2.60/

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ๐ŸŽ‰ Release Pipeline Complete โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                                                                                             โ”‚
โ”‚                                                 ๐ŸŽ‰ Pipulate Release Summary                                                 โ”‚
โ”‚  โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ  โ”‚
โ”‚  โ”‚ Component                 โ”‚ Details                                                                    โ”‚    Status    โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ ๐Ÿค– gemma3:latest Message  โ”‚ chore: Update version number in __init__.py and pyproject.toml             โ”‚      โœจ      โ”‚  โ”‚
โ”‚  โ”‚                           โ”‚                                                                            โ”‚ gemma3:lateโ€ฆ โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ ๐Ÿ“ฆ Version                โ”‚ 2.60                                                                       โ”‚    โœ… Set    โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ ๐Ÿš€ PyPI Release           โ”‚ https://pypi.org/project/pipulate/2.60/                                    โ”‚   โœ… Live    โ”‚  โ”‚
โ”‚  โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค  โ”‚
โ”‚  โ”‚ โฐ Completed              โ”‚ 2026-09-20 17:36:02                                                        โ”‚   ๐ŸŽฏ Done    โ”‚  โ”‚
โ”‚  โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ  โ”‚
โ”‚                                                                                                                             โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
๐Ÿ”„ server.py touched; the watchdog restarts the server.
(nix) pipulate $ 

And now thisโ€ฆ

Last login: Sun Sep 20 17:42:47 on console
michaellevin@MichaelMacBook-Pro ~ % rm -rf npvg                       
michaellevin@MichaelMacBook-Pro ~ % rm -rf ~/.config/pipulate         
michaellevin@MichaelMacBook-Pro ~ % curl -fsSL https://npvg.org | bash
Npvg -> ~/npvg   (to remove it later: rm -rf ~/npvg)
Deploy key saved to .ssh/rot (public, pull-only: it lets this folder fetch updates without a GitHub account).
To come back later:  cd ~/npvg && nix develop
Hydrating the Nix environment (the first time can take a few minutes)...
โœ… Transformed into a git repository (pre-transform files backed up to /tmp/nix-shell.S7WI3h/tmp.Cr3EIPpEx4).
Already up to date.
Found ~/.ssh/id_rsa -- switching the git remote to SSH so auto-updates use it.
(Determinate Nix 3.19.1) 2.34.6 ยท Python 3.12.13 ยท v2.60 ยท ~/npvg
๐Ÿ”ง Fresh install detected โ€” packages downloading...
โœ… 291 packages ready.
๐Ÿ““ 12 starter file(s) copied into Notebooks/ -- yours to edit.

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:nix-shell-env) (nix) npvg $ walk
Trail resolved: assets/trails/public_walk.json

This walk opens three public pages. Nothing to sign in to.
Return here and type CAPTURE when prompted at each page.
After all three captures and successful checks, it saves a private summary
and replaces your clipboard. Over SSH it uses a bridge file.
Nothing is sent to a chatbot. Review the summary before sharing it.

Choose a walk:
  1  Practice - read the steps; no pages open.
  2  Start the walk - open the pages.
  q  Exit (Enter also exits).
Choice: 2
Riding trail 'public_walk' -- 3 stop(s).

Make sure your audio is turned on.

The steps can be read aloud.

The voice is Piper, a small text-to-speech program that runs on this
computer; it was made for a Raspberry Pi. It reads sentences a person
typed in advance. No AI writes them, and nothing you say or type is
sent anywhere. Answering yes downloads the voice once, about 60 MB.

Read the steps aloud? [y/N] y

This walk opens three pages. You do not need to click anything. At each page, return here and wait for the CAPTURE prompt. Type CAPTURE and press Enter.
Downloading the voice, about 60 MB, once...
Voice ready.
  (If you did not hear that, turn your volume up.)

Summary file: data/decant-preview.md (private; replaced on save).
Checks can miss private details. A blocked check leaves the older file alone.

--- Stop 1/3: the_word ---

Opening page one in the browser. Leave the browser open; it closes on its own after capture. When the page is ready, return here and type CAPTURE.
Opening the browser; waiting for the page...

The page is open. When it looks finished, come back to the terminal and type CAPTURE.
Any other response aborts without capturing artifacts.
CAPTURE> CAPTURE
  LOCAL ARCHIVE  /Users/michaellevin/npvg/data/captures/walk-hcovsd0g/captures.md  (directory 0700, file 0600)
  Captured. final_url=https://npvg.org/walk/1/ artifacts=12
  ADVANCE -> next stop.

--- Stop 2/3: the_receipt ---

Opening page two in the browser. Leave the browser open; it closes on its own after capture. When the page is ready, return here and type CAPTURE.
Opening the browser; waiting for the page...

The page is open. When it looks finished, come back to the terminal and type CAPTURE.
Any other response aborts without capturing artifacts.
CAPTURE> CAPTURE
  Captured. final_url=https://npvg.org/walk/2/ artifacts=12
  ADVANCE -> next stop.

--- Stop 3/3: the_two_pages ---

Opening the last page in the browser. Leave the browser open; it closes on its own after capture. When the page is ready, return here, type CAPTURE, and read the result.
Opening the browser; waiting for the page...

The page is open. When it looks finished, come back to the terminal and type CAPTURE.
Any other response aborts without capturing artifacts.
CAPTURE> CAPTURE
  Captured. final_url=https://npvg.org/walk/3/ artifacts=12
  ARCHIVE STATUS  complete

Local archive file line for context.md or adhoc.txt:
/Users/michaellevin/npvg/data/captures/walk-hcovsd0g/captures.md
Review locally before compiling; raw bytes are not a safe disclosure.
  WALK ROUTER  /Users/michaellevin/npvg/adhocwalk.txt  (0600; names the UNSANITIZED archive until a preview is released)

Ride complete. Every stop produced a capture receipt.

Checking the summary before saving it and trying the clipboard.
   Preview checks: substitutions=0 denylist=0 secrets=0
   LOCAL PREVIEW /Users/michaellevin/npvg/data/decant-preview.md (0600; sha256=bdab0a47a7ee132159b978632ea6f5f24177c1491b7ccfbf0a9ab30d668ac95b)
   WALK ROUTER  /Users/michaellevin/npvg/adhocwalk.txt  (0600; the preview rides, the archive is commented beneath it)
Markdown output copied to clipboard
   Read the save and copy messages above; either step can fail.
   Review the summary before sharing it. You choose what to send.

The three-page walk is finished. Everything it captured is saved on this computer, and the lines above say whether the summary was saved and copied. Next, type epr: the letters e, p, r. That opens a text editor called vim, showing the list of files an AI will read. The keys j and k move the cursor. To leave, press Escape, then type colon q, then Enter. The notes at the top of that file say the same. Goodbye.
--------------------------------------------------------------
   CAPTURE RUN FINISHED
--------------------------------------------------------------
 Read the save and copy messages above. Either step can fail.
 Review the summary before sharing it.
 Nothing was sent to a chatbot.
--------------------------------------------------------------

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:nix-shell-env) (nix) npvg $ epr
(nix:nix-shell-env) (nix) npvg $

Okay, so first friggin WOW! And second, friggin WOW! The contents of the router file are just perfect. This is the moment of running behind the person riding the bicycle holding on preparing to let go.

  1 # ATTENTION HUMAN: this is the Prompt Router. epr edits it, cpr compiles it.
  2 # Welcome to vim (Neovim). This file was written by the last walk.
  3 
  4 # j moves the cursor down, k moves it up: the same two keys as in less.
  5 # To leave: press Esc, then type  :q  and press Enter.
  6 # Changed something by mistake? Esc, then  :q!  and Enter leaves it unsaved.
  7 # Esc is always the way back to safety.
  8 # Old joke: the best random-string generator is a beginner told to exit vi.
  9 
 10 # Every line below that does not start with # is one thing the AI will read:
 11 # a file path, or a shell command written after `! `.
 12 # introduction.md tells the AI what this system is. Keep it.
 13 # decant-preview.md is the checked summary of your walk. Keep it.
 14 # captures.md is the raw UNSANITIZED archive; it is big and may hold secrets.
 15 # Leave the # in front of it until an AI says it needs it, then read it first.
 16 
 17 # --- BEGIN LISTING FILES OR `! ` COMMANDS ---
 18 /Users/michaellevin/npvg/introduction.md
 19 /Users/michaellevin/npvg/data/decant-preview.md
 20 # /Users/michaellevin/npvg/data/captures/walk-hcovsd0g/captures.md
 21 
 22 # Next: Esc, :q, Enter, then type  cpr  at the prompt.
 23 # To add a line later: i starts typing, Esc stops, :wq saves and leaves.
 24 # The next walk rewrites this file (shorter notes) unless you have edited it.
~
~
~
~
~
~
~
~
~
~
adhocwalk.txt                                                                                        16,0-1         All
init.lua loaded successfully!                                                                                  

This is good
And this is right.
This is where I wanted to get to tonight!

The rest is just getting the commands into that. The rest is just shaping this around the day-job work I need to do. There might be a little language regarding quality assuring the output of AI so that they know what theyโ€™re looking at. This is the medicine to cure what ails you if what ails you is everyone starting to say โ€œBecause Claude said blah blahโ€ฆโ€

This is the โ€œOh yeah, weโ€™ll just see about thatโ€ antidote. Even Claude can see that! Those next details we will work out in the next article. Set the stage for hitting the ground running on the next article.

Dismount: The Notary Beat and the Living Record

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.

Fable 5.1 Extra:

1. VERIFY

The goal at the top of the article was training wheels for the newcomerโ€™s first vim, with a designed letting-go moment, delivered as gently as the less lesson; it grew mid-ride to include the spoken closing, the volume line, and introduction.md. Met, on this compileโ€™s receipts:

  • Writer straddle: BEFORE TypeError: unexpected keyword argument 'first' (your hand-run); AFTER long 24 18 and short 14 10, both exactly as predicted, uncommented lines introduction.md and the throwaway preview in both.
  • Guard: AFTER ValueError: hand-edited router kept, nothing written ... (or delete the file and the next walk rewrites it) and the ! ls line surviving, 1. The open branch was exercised by the two successful writes above it, with the introduction line present; the closed branch by the refusal. Both branches witnessed, per REFUSAL-ONLY WITNESS.
  • Next, type epr 0 to 1; turn your volume up 0 to 1; wc -l introduction.md from โ€œNo such fileโ€ to 45, the count predicted from the body as emitted.
  • Census lines 0 and 0: index.html and flake.nix untouched, as this ride intended.
  • Operator lane, the Mac on v2.60: (If you did not hear that, turn your volume up.) printed directly under Voice ready.; the closing printed in full; epr showed 24 lines, 18 comments, two paths, the archive commented beneath. Counted off your screenshot, it matches the compile laneโ€™s long 24 18 line for line: two lanes, one reading.

Ignition: none was required and none was skipped. Each walk is a fresh process, each probe imported the file at call time, and the Macโ€™s auto-update pull is visible in the transcript as v2.60. The earlier init.lua car is witnessed on the Mac too: gutter 1..24, no mode line.

Not witnessed: the closing as heard (the transcript prints it; the ear is not in words); the second walk on the same install writing the three-line header (the compile lane forced first=False and got it, the ride-lane cache has only its long branch on the Mac); cpr carrying introduction.md to a chatbot.

2. BANK

One car. You asked where to put โ€œeverything youโ€™re gonna needโ€ so a model cannot miss it, and named apply.py because it rides the fixed tail of every ADHOC compile. Yes, and the ride itself is the argument: the rule about a needed file being absent nearly could not be banked, because foo_files.py is absent. apply.py is here. The checklist clause in prompt_foo.py is the eventual home, read at the highest-attention position of every full-frame compile; that is next ride, with the file in context.

Target: apply.py
[[[SEARCH]]]
# ONE TIMELINE, NO PREVIEWS: every command in the response appears exactly once, in the section where it executes, in execution order -- PROBES (read-only) first, then PATCHES as numbered cars with any dependent actuator or ignition as the LAST cars, then PROMPT. A command that depends on a patch is never quoted above that patch, not as a preview, not "for later"; if it must be mentioned early, name it ("Car 3, the smoke") and show nothing.
[[[DIVIDER]]]
# ONE TIMELINE, NO PREVIEWS: every command in the response appears exactly once, in the section where it executes, in execution order -- PROBES (read-only) first, then PATCHES as numbered cars with any dependent actuator or ignition as the LAST cars, then PROMPT. A command that depends on a patch is never quoted above that patch, not as a preview, not "for later"; if it must be mentioned early, name it ("Car 3, the smoke") and show nothing.
# NEXT CONTEXT IS THE WHOLE LIST (banked 2026-09-20, convicted the same day,
# and banked HERE because this file rides the fixed tail of every ADHOC
# compile while the constitution does not): the payload a model is reading is
# the ONLY raw source it will ever be allowed to patch, and the operator
# comments the 40K-foot block out on turn two. A file that "is already there"
# this compile is gone next compile. Every file a next-turn car will patch is
# named in full in NEXT CONTEXT -- one that rode this compile, one that rides
# every compile, one the operator knows by heart -- because "already there"
# is a claim about the past wearing the future's label. CONVICTION: a
# three-car caboose said init.lua, flake.nix and index.html were already
# there; the next payload carried mother_cat.py and none of them, and two
# cars waited a turn for raw source the list had waved at. The cost is one
# line per file. THE GROOVE THAT HOLDS: the checklist's own NEXT CONTEXT
# clause in prompt_foo.py is the eventual home, read at the highest-attention
# position of every full-frame compile; this comment is the second groove,
# and a ยง key in foo_files.py the third.
[[[REPLACE]]]

3. DANGLING

  • The wheels coming off in the ride lane: a second walk on the same Mac install should write the three-line header; only the compile lane has seen first=False.
  • The closing as heard, and cpr on the Mac with introduction.md riding first, and a chatbotโ€™s answer to that payload.
  • index.html: โ€œInside less, j moves down, k moves up, and q quits,โ€ plus โ€œThe first commandsโ€ to โ€œThe first commandโ€; ignition ./nixops.sh, no rebuild; the census reads 0.
  • flake.nix: neovim in commonPackages, and ROUTER_TAIL gains the :Tutor line in the same fence; gate on both nix eval twins; ignition exit then nix develop; the census reads 0.
  • prompt_foo.py: the checklistโ€™s clause (2) NEXT CONTEXT gains one sentence, the ruleโ€™s real home.
  • foo_files.py: claim introduction.md under VIII-b HAND OFF (the Paintbox reads 204/274 with it unclaimed); a ยง key for NEXT CONTEXT IS THE WHOLE LIST; the ai.py specimens from this ride (chore: Handle voice disclosure failure messages for a volume hint, chore: Update introduction.md for a file that was created).
  • init.lua loaded successfully! still sits on the message line of the Mac epr screen; unasked, unfixed.
  • boot_menu.pyโ€™s epr line may name the editor in one word.
  • The transport rewrote line-leading -- as -- [ ] - [ ] - in two pasted transcripts and left this one alone; a second canary shape for prompt_foo.py, unruled.
  • The 2026-09-16 drill-down TODOโ€™s header half is discharged (โ€œnever replacedโ€ is gone); the guardโ€™s swap edge remains.
  • _ROUTER_FIRST is process state: a test importing mother_cat twice in one process resets it or passes first.

4. SEED

# --- THE WHEELS COME OFF: less teaches its keys, the flake pins the editor, the rule finds its home ---
remotes/honeybot/www/npvg.org/index.html
flake.nix
scripts/mother_cat.py
prompt_foo.py
foo_files.py
! grep -ciE 'moves (down|up)|press q' remotes/honeybot/www/npvg.org/index.html
! grep -c 'neovim' flake.nix
! grep -c ':Tutor' scripts/mother_cat.py
! grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py prompt_foo.py foo_files.py
! LD_LIBRARY_PATH= nix eval --raw .#devShells.aarch64-darwin.quiet.drvPath | tail -c 44
! LD_LIBRARY_PATH= nix eval --raw .#devShells.x86_64-linux.quiet.drvPath | tail -c 44

5. CLOSING

The v in NPvg is vim, and this ride put it there for a stranger. Twenty-four lines, eighteen of them comments, counted before they were cut; the first router a machine writes carries the lesson, the second one does not, and the moment between them is the hand coming off the back of the bicycle.

You asked whether you were talking out of your ass about the overnight ripcord. Not for this class of work, and the ride is the receipt. The spec moved twice inside it: a ruling from five days earlier (โ€œthe goodbye adds no command and no lessonโ€) was reversed in writing after you watched the Mac stop abruptly, and introduction.md did not exist as an idea until one message before it existed as a file. A definition of done written at t=0 would have shipped the old goodbye, faithfully, all night. The acceptance test for this work was your face at the epr screen, and that test cannot be written down before the screen exists. Be fair to the ripcord where it belongs: when done is a test that passes, let it run. When done is a stranger reading a screen, the chisel is the only instrument, and every strike wants its reading before the next.

Which is the whole answer to โ€œbecause Claude said so.โ€ The cure is not a smarter Claude; it is a receipt Claude cannot flatter. long 24 18 was predicted from a block before it was written and read back from a machine after, in two lanes that agreed. That is the difference between a claim and a reading, and it is the thing a newcomer will now learn to ask for, in vim, one line at a time.

6. NOTARIZE

Fingerprint of the sealed bytes, from this payloadโ€™s own Summary and Manifest: Total Bytes 484,003 (UTF-8), Verified Tokens 117,946, seven LIVE COMMAND RECEIPTS, Codebase 59,552 tokens across four files and seven receipts, newest dated line in the payload 2026-09-20 17:42:47 (the Macโ€™s login line) with the v2.60 bump in Telemetry. The deed is read off the envelope footer under this prompt, not guessed:

Deed: foo-d1cadc20-1552.zip (confirm: ls -t foo-*.zip head -1)

The seal is the signature; the fingerprint is how the deed is found. This discussion is closed.

MikeLev.in: Oh good Iโ€™m glad we got this:

(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 $ patch
(nix) pipulate $ app
โœ… DETERMINISTIC PATCH APPLIED: Successfully mutated 'apply.py'.
(nix) pipulate $ d
diff --git a/apply.py b/apply.py
index 2c160069..1606794b 100644
--- a/apply.py
+++ b/apply.py
@@ -15,6 +15,22 @@ import os
 import subprocess
 
 # ONE TIMELINE, NO PREVIEWS: every command in the response appears exactly once, in the section where it executes, in execution order -- PROBES (read-only) first, then PATCHES as numbered cars with any dependent actuator or ignition as the LAST cars, then PROMPT. A command that depends on a patch is never quoted above that patch, not as a preview, not "for later"; if it must be mentioned early, name it ("Car 3, the smoke") and show nothing.
+# NEXT CONTEXT IS THE WHOLE LIST (banked 2026-09-20, convicted the same day,
+# and banked HERE because this file rides the fixed tail of every ADHOC
+# compile while the constitution does not): the payload a model is reading is
+# the ONLY raw source it will ever be allowed to patch, and the operator
+# comments the 40K-foot block out on turn two. A file that "is already there"
+# this compile is gone next compile. Every file a next-turn car will patch is
+# named in full in NEXT CONTEXT -- one that rode this compile, one that rides
+# every compile, one the operator knows by heart -- because "already there"
+# is a claim about the past wearing the future's label. CONVICTION: a
+# three-car caboose said init.lua, flake.nix and index.html were already
+# there; the next payload carried mother_cat.py and none of them, and two
+# cars waited a turn for raw source the list had waved at. The cost is one
+# line per file. THE GROOVE THAT HOLDS: the checklist's own NEXT CONTEXT
+# clause in prompt_foo.py is the eventual home, read at the highest-attention
+# position of every full-frame compile; this comment is the second groove,
+# and a ยง key in foo_files.py the third.
 
 # THE SCREENSHOT IS NOT A LOCATOR (banked 2026-09-11, two exchanges lost).
 # Botify's Confluence pages for custom link attributes say "go to the project
(nix) pipulate $ m
๐Ÿ“ Committing: chore: Update documentation for apply.py
[main e3a9cd7e] chore: Update documentation for apply.py
 1 file changed, 16 insertions(+)
(nix) pipulate $ git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 48 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 995 bytes | 995.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (2/2), completed with 2 local objects.
To github.com:pipulate/pipulate.git
   dda6b9d1..e3a9cd7e  main -> main
(nix) pipulate $ 

Book Analysis

Ai Editorial Take

The most compelling innovation here is the concept of โ€˜self-shedding scaffolding.โ€™ Rather than forcing users into a permanent beginner mode or abandoning them to raw defaults, the software inspects its own state: if a file has never been seen, it dispenses rich contextual guidance and the classic exit jokes; once the user completes a ride or edits the file, the training wheels dissolve into a concise reference header. This approach respects human cognitive myelination instead of treating UI assistance as a permanent crutch.

๐Ÿฆ X.com Promo Tweet

Handing someone Vim for the first time without guidance is a rite of terror. Here is how we engineered self-shedding training wheels for prompt routing in the Age of AI: https://mikelev.in/futureproof/vim-training-wheels-and-the-letting-go-moment/ #Vim #DevOps #ProductDesign

Title Brainstorm

  • Title Option: The Letting-Go Moment: Building Vim Training Wheels for the Age of AI
    • Filename: vim-training-wheels-and-the-letting-go-moment
    • Rationale: Directly references the bicycle metaphor used in the piece and highlights the balance between beginner empathy and terminal mastery.
  • Title Option: Onboarding Empathy: Designing the Newcomerโ€™s First Terminal Threshold
    • Filename: onboarding-empathy-first-terminal-threshold
    • Rationale: Focuses on the ergonomics of command-line tools and how thoughtful guidance prevents early user abandonment.
  • Title Option: Beyond the Overnight Ripcord: The Case for Craftsmanship in AI Tooling
    • Filename: beyond-overnight-ripcord-craftsmanship-ai-tooling
    • Rationale: Highlights the philosophical contrast between unmonitored agentic code generation and deliberate, turn-by-turn verification.

Content Potential And Polish

  • Core Strengths:
    • Vivid documentation of multi-platform verification across Linux workstations and macOS environments.
    • Deeply empathetic insight into user experience: recognizing that Vimโ€™s default modal quirks can completely alienate an unfamiliar user.
    • Clear demonstration of unrolled verification turns with before-and-after command receipts.
  • Suggestions For Polish:
    • Incorporate a dedicated visual diagram or ASCII sketch showing the transition from initial walk router to the permanent lean router once training wheels come off.
    • Clarify the exact role of introduction.md for external readers who encounter the repository payload cold.

Next Step Prompts

  • Review the text of introduction.md to ensure that paths, instructions, and privacy rules remain completely model-agnostic across ChatGPT, Claude, and Gemini.
  • Draft the subsequent commit fence to verify that running a second walk on the macOS installation properly writes the abbreviated three-line header without regressing user edits.