The Letting-Go Moment: Building Vim Training Wheels for the Age of AI
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:
- A bunch of stuff has been captured and is on disk.
- The next step is typing
eprto enter NeoVim where you will see these files listed. That is the router of the context compiler. You add lines here. - 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:
- 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.
- 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.
- 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.
- DANGLING: what carries forward unbanked? One line each, no essays.
- SEED: the adhoc.txt lines (and TODO_SLUGS if narrative context is needed) for the next rideโs first compile.
- 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.
- 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); AFTERlong 24 18andshort 14 10, both exactly as predicted, uncommented linesintroduction.mdand 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! lsline 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 epr0 to 1;turn your volume up0 to 1;wc -l introduction.mdfrom โNo such fileโ to45, the count predicted from the body as emitted.- Census lines 0 and 0:
index.htmlandflake.nixuntouched, as this ride intended. - Operator lane, the Mac on v2.60:
(If you did not hear that, turn your volume up.)printed directly underVoice ready.; the closing printed in full;eprshowed 24 lines, 18 comments, two paths, the archive commented beneath. Counted off your screenshot, it matches the compile laneโslong 24 18line 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
cpron the Mac withintroduction.mdriding 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:neovimincommonPackages, andROUTER_TAILgains the:Tutorline in the same fence; gate on bothnix evaltwins; ignitionexitthennix 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: claimintroduction.mdunder 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 messagesfor a volume hint,chore: Update introduction.mdfor a file that was created).init.lua loaded successfully!still sits on the message line of the Maceprscreen; unasked, unfixed.boot_menu.pyโseprline 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 forprompt_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_FIRSTis process state: a test importingmother_cattwice in one process resets it or passesfirst.
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.
- Filename:
- 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.
- Filename:
- 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.
- Filename:
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.