Local-First Development and Resilience in the Age of AI
Setting the Stage: Context for the Curious Book Reader
This chapter is an important window into how local-first tooling and reproducible environments protect your daily work when grid infrastructure fails. Written during a midnight blackout on Staten Island, it traces how a simple git checkout, a mobile notebook, and standard Unix utilities allowed development to continue seamlessly without cloud dependencies.
TL;DR: A power outage at midnight took down the author’s home-hosted web server and the machine holding his working notes. He kept working from a laptop on a phone hotspot: a phone notebook verified the public installer, the laptop pulled the latest code from GitHub, and the project’s AI-assisted patch workflow ran from the laptop for the first time. That first run on a second machine exposed bugs the main machine had been hiding: three scripts computing the repository root one folder too high after a directory move, two scripts printing outdated paths, a statistics writer that overwrote a tracked file with zeros, and a telemetry fetch that reported success on an empty pipe. All were fixed and pushed. A forced push in the dark orphaned one commit, which was found and re-applied. An accidental commit of a scratch file was caught and reverted. The article also explains why Mac keyboard shortcuts differ from Linux ones, and why that difference is what lets vim work unchanged on both.
Technical Journal Entry Begins
🔗 Verified Pipulate Commits:
MikeLev.in: It is 2 AM on Monday morning September 21st and I deliberately went to sleep at about 8 PM to wake up as early as I could to work straight through the night. I woke up at about midnight naturally trying to ask Alexa what time it was and it would not respond and I heard wind and rain outside and was like uh huh. Perfect timing. I’m about to close the loop on my work having brought it right up to the edge of useful in my day-to-day responsibilities now in my new job at work with a meeting I could’ve shown how many Jira cases I fantasy league ticket closing ran it against.
And there is a blackout.
I reported it to ConEdison who promptly called me back at like 1 AM to their credit even though I only asked for updates over text. Looking out both my windows it very much appears the power is out for everyone except I can see the street lights on on the major thoroughfare one side of my apartment looks out on. That’s the side of the electricity comes from which also has the meter which I also have no access to because it’s in the yard of my downstairs neighbor. Ugh! ConEdison said the next-door neighbors have power so it’s probably a problem in my unit. I threw the circuit breakers and that’s not it so the problem is from the outside but I’m not gonna have ConEdison disturbed my downstairs neighbors in the middle of the night.
This is Murphy’s Law hitting full force.
Surviving the Grid Failure Through Local-First Architecture
This is also one of the downsides of home hosting because my website is also unreachable until power comes back, ha ha. I’m paying for all this with a bit of pain right now and I shall make it pay me back by sharing the story with the world as part of this future proofing in the age of AI book. Your stories belong to you. Share them if you want to.
So this is the worst kind of time in the world right now where I absolutely could be doing the work I need however the dependency on the power grid becomes amazingly obvious.
However in a beautiful demonstration of local-first even without UPS backups, both my phone and my Mac laptop which I’ve been testing my work on are fully charged. And because I’ve been testing my work on the Mac, it actually has the very latest git repo pulled down locally so I can actually continue working on the Mac straight into middle of this rainy late September Monday night.
It was in the bleak September; work near finished I remember. I woke up at midnight to drive the final pieces home but found the power was no more. Worry not. I’m up to chore.
First on Simplenote I jotted how the product that I plotted Could be worked on with no power at 40,000 feet as Linus said before. What can stop me? Nothing more. Git is nice. It’s what it’s for.
Verifying State on a Mobile Device
56 years and 7 months Steve Jobs was when that cancer got him 56 years and one month I am and just beginning; I am not him And so without the world upon my shoulders I tap on iPhone in the dark With a kitty I adore Purring loudly I am not less perfect than Lore.
I have literally closed the loop on my work. After a fresh install of the Pipulate project on their machines even though that is not what people really know that they are doing when they install it after I teach them how to inspect the installer and start the vi / vim / NeoVim education also unbeknownst to them with the command:
curl -fsSL https://npvg.org | less
…to start teaching them how to navigate using down and up using j and k and to quit using q…
Do you see where I’m going with all of this? First I restate how beautifully this all has come together and how the loop is finally closed and held the user, even one who is new to the Unix-like terminal and new to vim can actually start working from where all this leaves off.
npvg.org is down too and I was going to say or else I could show you how that works even now during the blackout because I can use an iPhone app terminal to curl but instead I will say I can still show you how this works because pipulate.com is still hosted on GitHub Pages and is exactly the same code even though I can’t control the web server there the way I do control Nginx on npvg.org at home. Yet still this means I am not stopped. I’ve hedged my bet and I have the alternative.
I shall use not a terminal program on my iPhone but Python in a Jupyter notebook using Carnets plus which is about as close to FOSS software on the iPhone as it gets and I’ve used it for years.
Hey Fabel 5.1 please write a tiny example python script I can copy paste into a notebook to demonstrate how not cut off from developing this article and educating the world about this stuff I am even in the middle of the night in the middle of a blackout on the middle of an island, Staten though it might be.
It is now 2:30 AM.
Fable 5.1 Extra:
Yes, I see where you’re going, and the blackout wrote the paragraph for you: the home-hosted door went dark and nothing you had built went dark with it. The repo is on the Mac because you were testing on the Mac. The installer is on GitHub Pages because release.py has been copying it there on every release. The book is in a text file on a phone. Local-first was never a slogan about servers; it was a bet that the work survives the grid, and at 2 AM on Staten Island the bet paid.
Here’s the notebook version of curl -fsSL https://npvg.org | less, standard library only so Carnets+ needs nothing. First cell fetches and gives you the receipt; second cell is the j key.
import hashlib, urllib.request
# The same installer npvg.org serves, from the door that is not dark tonight.
url = "https://pipulate.com/install.sh"
body = urllib.request.urlopen(url, timeout=20).read()
text = body.decode("utf-8")
lines = text.splitlines()
print(f"{len(body):,} bytes, {len(lines)} lines")
print("sha256", hashlib.sha256(body).hexdigest()[:16], "...")
# The npvg door stamps one placeholder to "npvg"; this door leaves it alone.
print("door:", "unstamped (pipulate)" if "__INSTALL_DEFAULT_" "NAME__" in text else "stamped")
pos = 0
def page(n=20):
"""j, twenty lines at a time. Run again for the next twenty. Stop running it: that is q."""
global pos
for i, line in enumerate(lines[pos:pos + n], start=pos + 1):
print(f"{i:4d} {line}")
pos += n
page()
What it proves, for the article: a reader with no terminal, no Nix, no power at home, and a phone can still pull the exact bytes a stranger’s install would run, hash them, and read them twenty lines at a time, which is the whole less lesson minus the keyboard. When the lights come back, curl -fsSL https://npvg.org | sha256sum will differ from this hash by exactly one word, and you already know which one.
Go back to sleep if you can. The cat has the right idea.
Me: Back to sleep, I wish I could. The early bird trick in morning would Have made me productive in leaps and bounds on vampire time as it should. With all the high energy food I ate to store. These bits washed up on my shore:
16,852 bytes, 353 lines
sha256 f3181426f3ae92c9 ...
door: unstamped (pipulate)
Now if I cannot return to sleep I pray ideas I shall keep producing but not merely that because ideas are cheap and it’s implementation I’m aiming for without stress despite surprises fate had in store.
So I make a hotspot with my phone and now I’m typing in Simplenote on my Mac continuing on the same note I was in before.
I allow low-power mode to be able to turn on again which I normally turn off because I hate the brightness of the screen auto-fading on me. I call that going Mac-blind. Even when your battery is at full power and you’re connected for some reason the Mac default is always to turn down brightness on you, even in night mode when the screen is black and all you want is your fat white over-sized text as high contrast as you can make it for your old eyes.
So there. Both batteries at max and I can take these notes over to NeoVim where it belongs.
command + n to bring a new tab up in the Mac terminal which I always have on the first virtual workspace as the office.txt journal, but that’s the terminal I’ve been testing in so now I have a Mac terminal with 2 tabs. But here’s the trick because I want to go from one full-screen Mac terminal (makes the Mac usable) to 2 full-screen terminals with no tabs. You just grab the 2nd tab and drag it way and BAM! 2nd full-screen terminal. That’s a small consolation prize for being on the Mac. There are occasionally these nice conveniences but of course it’s secret years-long platform lock-in compelling that you have to fight to learn these tricks.
That’s an interesting place to be — knowing those little Mac tricks like now top type an em-dash with option + Shift + - without actually being a so-called Mac person. That’s the best of both worlds. But don’t fill your head and muscle memory with too many of those secret cabal club moves or Apple’ll getcha good.
Platform Quirks and the Mechanics of Muscle Memory
Last login: Sun Sep 20 17:43:02 on ttys000
michaellevin@MichaelMacBook-Pro ~ % cd ~/repos/office
michaellevin@MichaelMacBook-Pro office % vim office.txt
michaellevin@MichaelMacBook-Pro office % cd ~/repos/pipulate
michaellevin@MichaelMacBook-Pro pipulate % nix develop .#quiet
warning: Git tree '/Users/michaellevin/repos/pipulate' has uncommitted changes
(nix:nix-shell-env) (nix) pipulate $ git pull --force
remote: Enumerating objects: 586, done.
remote: Counting objects: 100% (263/263), done.
remote: Compressing objects: 100% (146/146), done.
remote: Total 586 (delta 173), reused 203 (delta 117), pack-reused 323 (from 1)
Receiving objects: 100% (586/586), 1.13 MiB | 6.18 MiB/s, done.
Resolving deltas: 100% (363/363), completed with 12 local objects.
From github.com:miklevin/pipulate
999788d4..e3a9cd7e main -> origin/main
Updating 999788d4..e3a9cd7e
Fast-forward
AI_CONTEXT.md | 52 +++--
GLOSSARY.md | 309 ++++++++++++++++++++++++-
Notebooks/.agents/skills/gsc_readonly/SKILL.md | 16 +-
Notebooks/.agents/skills/sheets_readonly/SKILL.md | 10 +-
__init__.py | 4 +-
apply.py | 33 ++-
apps/010_introduction.py | 16 +-
assets/axis_ledger.jsonl | 1 +
assets/installer/mck.sh | 11 +-
assets/trails/botify_pageworkers.yaml | 6 +-
assets/trails/first_context.yaml | 6 +-
assets/trails/jira_for_you.yaml | 2 +-
assets/trails/public_walk.json | 12 +-
assets/trails/se_ticket.yaml | 10 +-
assets/trails/ticket.yaml | 4 +-
{scripts/connectors => connectors}/README.md | 0
{scripts/connectors => connectors}/botify.py | 42 ++--
{scripts/connectors => connectors}/confluence.py | 0
{scripts/connectors => connectors}/gmail.py | 6 +-
{scripts/connectors => connectors}/gsc.py | 30 +--
{scripts/connectors => connectors}/jira.py | 28 +--
{scripts/connectors => connectors}/mcp.py | 20 +-
{scripts/connectors => connectors}/mcp_warm.py | 18 +-
{scripts/connectors => connectors}/noop.py | 2 +-
{scripts/connectors => connectors}/sheets.py | 36 +--
{scripts/connectors => connectors}/slack.py | 20 +-
{scripts/connectors => connectors}/wallet.py | 48 ++--
flake.nix | 179 ++++++++-------
foo_files.py | 546 ++++++++++++++++++--------------------------
imports/voice_synthesis.py | 240 +++++++++++++++++--
init.lua | 39 +++-
introduction.md | 45 ++++
pipulate/core.py | 2 +-
prompt_foo.py | 82 ++++++-
pyproject.toml | 2 +-
requirements.in | 2 +-
scripts/articles/articleizer.py | 75 ++++--
scripts/articles/gsc_historical_fetch.py | 2 +-
scripts/boot_menu.py | 305 ++++---------------------
scripts/continuation_ladder.py | 6 +-
scripts/gsc/gsc_keyworder.py | 2 +-
scripts/gsc/gsc_top_movers.py | 2 +-
scripts/map_sheet.py | 2 +-
scripts/mcp_dummy_server.py | 6 +-
scripts/mother_cat.py | 194 ++++++++++++----
scripts/sources_menu.py | 14 +-
scripts/two_arm.py | 6 +-
scripts/weblogin.py | 2 +-
server.py | 2 +-
tools/connector_tools.py | 8 +-
tools/mcp_tools.py | 2 +-
tools/scraper_tools.py | 134 +++++++----
52 files changed, 1624 insertions(+), 1017 deletions(-)
rename {scripts/connectors => connectors}/README.md (100%)
rename {scripts/connectors => connectors}/botify.py (93%)
rename {scripts/connectors => connectors}/confluence.py (100%)
rename {scripts/connectors => connectors}/gmail.py (98%)
rename {scripts/connectors => connectors}/gsc.py (91%)
rename {scripts/connectors => connectors}/jira.py (96%)
rename {scripts/connectors => connectors}/mcp.py (97%)
rename {scripts/connectors => connectors}/mcp_warm.py (97%)
rename {scripts/connectors => connectors}/noop.py (98%)
rename {scripts/connectors => connectors}/sheets.py (92%)
rename {scripts/connectors => connectors}/slack.py (97%)
rename {scripts/connectors => connectors}/wallet.py (97%)
create mode 100644 introduction.md
(nix:nix-shell-env) (nix) pipulate $ cd ../office/
(nix:nix-shell-env) (nix) office $ vim office.txt
There, that’s better. See how I bring a piece of my hardware infrastructure around using the Pipulate project? Inform the nice people of all the Future Proofing Yourself in the Age of AI shenanigans you see at play here in this article so far and the recent article you know has led up to this one.
Fable 5.1 Extra: What’s on display in this article, for the reader who came in at the blackout:
The grid failed and the work did not. The home server on npvg.org went dark at midnight with the rest of the block, and the only thing that went dark with it was one door. The repository was on the laptop because the laptop had been the test rig all weekend. The book was in a text file on a phone. Both batteries were full because nothing here needs a datacenter to run. That is what local-first means once you strip the slogan off it: the work is a pile of plain-text files that happen to be on the machine in front of you, and a power failure is a network partition, not a loss.
Two doors, one file. Pipulate’s installer is served from two addresses. The home Nginx door at npvg.org stamps one placeholder to “npvg” so the folder lands as ~/npvg; the GitHub Pages door at pipulate.com serves the file untouched. The Python cell that ran on the phone at 2:30 AM read 16,852 bytes, 353 lines and door: unstamped (pipulate). That byte count is the same 16,852 that sat in a payload ledger two days earlier beside assets/installer/install.sh in the repository. The publish lane delivered the repo file to the second door intact, and a phone verified it during a blackout without a terminal, using the only thing a stranger has: a URL and twenty lines at a time. The less lesson, the newcomer’s first three keys, survived being moved to a notebook on a phone. Text wins.
Git is the portable state. The pull on the Mac’s development checkout brought 52 files and 1,624 insertions across in one fast-forward, 999788d4..e3a9cd7e: the training-wheels header in mother_cat.py, the new introduction.md, the connectors moving to the repo root, the flake and router changes from the last two days. The Mac had two copies of Pipulate on it all weekend, ~/npvg as the stranger’s disposable install and ~/repos/pipulate as the workbench, and only the workbench needed to catch up. nix develop .#quiet then opened the same shell the Linux box opens, with the same Python and the same words in it. The one warning, that the tree had uncommitted changes, is Nix noticing the worktree was dirty before the pull; the fast-forward went through, so nothing was in the way.
The journal is the durable thing. Notes started in Simplenote on the phone, moved to Simplenote on the Mac over a hotspot, and then to office.txt in vim, where the book is actually written. Every stage of that was a text file, and the last stage is the same editor the previous article spent a weekend teaching a newcomer to leave. The tool teaches itself by being used in front of the reader.
Enough Mac to be productive, not enough to be owned. Dragging a tab out of a full-screen terminal to get a second full-screen terminal; the em dash on Option-Shift-hyphen; low-power mode dimming a screen that wants to be black with fat white text on it. These are real conveniences, and they are also years of platform-specific muscle memory that transfer nowhere. The muscle memory that transfers is vim, git, and Nix, which are identical on the Mac and the Linux box, which is why the blackout cost a server and not a workday. Learn the tricks; do not let them become the skill.
The loop this article closes. The previous article’s goal was the newcomer’s first vim. The walk now ends by saying what was captured and what to type next; epr opens a router whose first lines say how to move and how to leave, with the old joke about exiting vi; introduction.md rides first in the payload so the chatbot on the other end knows who compiled it and how to help; the second walk writes a shorter header, the wheels coming off by themselves. That was witnessed on a fresh Mac install Sunday evening. Tonight the author ran the same portability test on himself: a fresh pull, a fresh shell, a journal open, in the dark, on an island.
What home hosting costs, and what it pays. The site is unreachable until ConEdison finds the fault, and there is no CDN to hide that. The trade was made on purpose: a home server you control down to the Nginx config, a second door you don’t control but don’t need to, and a book that turns the outage into a chapter. Your stories belong to you. This one has a sha256 in it.
MikeLev.in: Exactly so! Can’t be stopped. What I really wanted to do now is get the
example jira and gmail commands and all into that tutorial but that doesn’t
make sense because those can’t be “for the world” templates because they are so
specific to how I need to use it for my day-job use case. I also can’t use my
2nd brain because it uses a folder full of Markdown files in
~/repos/trimnor/_posts/ which is particularly hard to reconstruct because the
machine that repo lives on is suffering the blackout (server with no universal
power supply). The webserver it’s served as a Jekyll website is likewise dark
because even though it is a laptop and sort of has its own built in UPS, that
juice has long ago been drained and my Verizon OTP and routers that plugs into
are also dark. So the 2nd brain is completely cut off. Same “no CDN” decision
bites me there too. Just pull it from GitHub? The trimnoir repo which is the
so-called raw book ore that is https://mikelev.in/ isn’t on GitHub! Pipulate is
on GitHub but the corpus of training material that is the Future Proofing in the
Age of AI book isn’t. I simulate GitHub Pages at home from git repo remote
endpoint through public publishing surface.
The upshot of all that is there will be no 2nd braining and I want to earmark to myself that there should be. I should have a contingency for that, probably another potentially GitHub Pages site that’s totally noindexed and nofollowed and disallowed through robots dot TXT and all that. I don’t want the git history exposed and you can’t keep private repos and publish public websites with GitHub. I stopped paying the hundred dollars a year or whatever it is Microsoft Tax on so-called GitHub Pro. I have discontinued I do believe every Microsoft subscription extortion tax from Office 365 (or is it Copilot 365 now?) through GitHub Pro. Woot! That’s at least $200 per year Microsoft no longer gets from me. I’ll also never buy another Microsoft laptop that I used to be a huge fan of until the unrepairability of them made broken keys and cracked screens totally trash a thousand dollar device. I know they’ve addressed that in newer laptops but it’s all compounded over the years into fool me ten times, I won’t get fooled an eleventh. I draw the line at being a fool eleven times. Mike-E Levin.
Okay then think! What’s your next step? How do you not squander this energy. Oh, make sure you can keep Prompt Fu’ing. And make sure there’s a chapter in the book about all the short commands and that they’re well understood.
Oh, and also getting to the organizational knowledge this is all ultimately about for the day-job, but then things are going to get proprietary. I should see what’s accessible from the public side already about my employer’s product to know what the boundaries are of what can be discussed in public. There’s nothing you can’t figure out from the outside looking at a site as a hacker.
First we establish that we can still Prompt Fu on the Mac and that the chapter about the short Unix-like commands are there and visible to the LLM. Let’s see…
Hey Fable 5.1 how did we do? What do you see?
Note: Okay while it’s working on that I want to take note that on the Mac the way you paste a large text-block into a browser text-area preventing it from becoming an attachment is
option+shift+Vwhich on any other platform would have beenCtrl+Shift+V. My muscle memory knows the equivalencies which are not really even equivalencies because Apple screwed the 1-to-1 mapping between weird Mac modifier keys and industry-standard keys too, otherwise I would be able to say that what I just said means that theoptionkey is the equivalent to theCtrlkey in the mainstream, but there’s acontrolkey right there on the Mac keyboard sandwiched between thefnkey and thecommandkey. See?
This is why it’s clearer in my muscle memory my fingers just knowing what to press when I’m on the Mac than verbalizing it with language which is much harder because it doesn’t seem to make any sense. Apple did not merely relabel the keys but they changed the semantic meanings too… and then they brought back the
Ctrlkey in the form of thecontrolkey and I’m going to guess that if a machine can’t run properly with vim muscle memory, it’s not a legitimate developer platform. It’s similar to how Windows is not a legitimate developer platform without WSL because Linux is where all the real grown-up developer toolchains live today (and not on Windows). Windows is really a gaming platform now to host proprietary drivers until Valve SteamOS erodes away that moat. Let’s go check on Fable 5.1’s response…
Fable 5.1 Extra: Context verified for the question you asked and for every car below. One file is missing for the earmark this compile itself surfaced, and I name it where it comes up: prompt_foo.py, the stats-block writer.
How we did
Prompt Fu works on the Mac in the dark. The receipt is this payload: 17 files, 102,946 verified tokens, 422,546 bytes, 37 references scanned and all present, ruff clean, sealed as foo-518c7579-01.zip. The -01 is the first cartridge this checkout has ever cut; the Linux box was at 1552 when the lights went out. Every path reads /Users/michaellevin/npvg, so this compile ran from the stranger’s install in the first terminal tab, not from the workbench you pulled at 2 AM. That matters twice below. No ! receipts rode, because no probes were in the router; the train’s probes are the first readings anyone has taken on this machine.
The chapter about the short commands, as I see it
It exists in three surfaces generated from one source, exactly as the README’s contract item 8 says: conn prints the roster (warm botify confluence jira slack email sheets, plus pu), the README carries the contract and the six auth kinds, and each connector’s first docstring line is the sentence beside its word. gsc is on PATH but held back from the roster on purpose; mcp is an alias; noop is deliberately absent everywhere. What is not in this payload is the other half of the chapter, menu walk conn epr cpr all brief jn pu, which lives in boot_menu.py and the flake.
Two rows of that chapter lie. confluence.py and gmail.py still teach their own use as python scripts/confluence.py ENG and python scripts/gmail.py <thread_id>, in their docstrings and in every # Next: breadcrumb. The move’s sed rewrote scripts/connectors/; these two never said that, so it could not see them. A newcomer who types conn, then confluence, then the command the output tells them to type next, gets “No such file.”
What the blackout exposed inside the compile
The stats block is a tracked-file write, and it fires on cpr. Telemetry shows the diff: this compile rewrote foo_files.py from “1,490 articles at MikeLev.in” to “0 articles at Mac Shadow.” Sunday’s cpr on the same install did the same. So every newcomer’s first cpr dirties a tracked file, and their next cd ~/npvg && nix develop hits the halt-don’t-destroy gate: “Local modifications detected. Skipping automatic update,” about work they never did. Their install stops updating on day two. INFERRED for the newcomer, witnessed on this Mac: ~/npvg is dirty right now for exactly that reason.
Maintaining Momentum Across Distributed Machines
The Honeybot line says “fetched” beside blanks. The block reads Honeybot telemetry fetched 2026-09-21T07:23Z, DOM hydration: trapdoor triggers from non-local IPs, and the Markdown negotiated line is gone. The server was dark; the fetch returned nothing; the writer wrote the word “fetched” anyway. That is the ATTRIBUTED-VOICE test failing inside the router at 3:23 AM, and the writer is in prompt_foo.py, which is not here.
The two checkouts have drifted. apply.py in this payload does not carry the NEXT CONTEXT IS THE WHOLE LIST comment the notary banked, so either that car never landed on Linux or ~/npvg has not pulled since Sunday 17:38 (the tab has been open since then). One probe below tells you which, after the pull.
What the move left behind
Three files compute the repo root as three parents up, which was right under scripts/connectors/ and now lands in $HOME: botify.py (project_root, masked because the editable install also exposes config), wallet.py (parents[2], masked inside the shell by PIPULATE_ROOT, wrong everywhere else), and mcp.py (_REPO_ROOT, not masked: every FDR receipt would land under ~/browser_cache/mcp/ instead of the repo’s gitignored one). And mcp_dummy_server.py still points CLIENT at scripts/connectors/mcp.py, so --selftest opens with its own ABORT line. All INFERRED from path arithmetic against the rename receipt in your pull; the first probe convicts or acquits all four at once.
The 2nd brain, and the boundary
The 2nd brain is not a website; it is _posts/, which is a git repo, and git repos clone. A noindexed GitHub Pages mirror would still be a public repo of the book’s history, which is what you do not want. The cheaper hedge is the one Pipulate already got: a second clone on the Mac. rgx’s own fallback path is ~/repos/trimnoir/_posts, so git clone from the Linux box when it wakes, and rgx works on the Mac with zero configuration; a read-only target in blogs.shadow.json gives posts -t the same. Earmarked below.
On the day-job side: the public surface of your employer’s product (docs, API reference, marketing) is the boundary, and !URL is the instrument for reading it. Before any ! jira or ! botify receipt compiles on this Mac, note that Sunday’s cpr read substitutions=MISSING; rm -rf ~/.config/pipulate took the scrub table with it. A connector receipt compiled here rides to the chatbot unscrubbed until scrub recreates that file. The last probe reads that state.
1. PROBES
Run these in ~/npvg, where this compile ran. Line one names files only; it reads ` M foo_files.py now and will read the same after the next compile, and that sameness is the conviction (the compiler re-dirties the file before the probes run). Line two reads 0 now; after Car 0's pull it reads 1 if the notary's BANK car landed on Linux, 0 if it never did. Lines three and four are the breadcrumb straddle, predicted 11 and 8 to 0 and 0, then 0 and 2 to 11 and 10, counted off the payload. Line five convicts the roots: three lines ending in your home directory now, three ending in npvg after. Line six reads the harness's own ABORT sentence now and # 20/20 checks passed after, with a third string if it produces neither. Line seven is a census, exit 1 both times unless you run scrub`.
git status --porcelain
grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
grep -c 'scripts/confluence.py' connectors/confluence.py; grep -c 'scripts/gmail.py' connectors/gmail.py
grep -c 'connectors/confluence.py' connectors/confluence.py; grep -c 'connectors/gmail.py' connectors/gmail.py
env -u PIPULATE_ROOT .venv/bin/python -c "import sys; sys.path.insert(0, 'connectors'); import botify, wallet, mcp; print('botify', botify.project_root); print('wallet', wallet.REPO_ROOT); print('mcp', mcp._REPO_ROOT)"
.venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
test -f "$HOME/.config/pipulate/pii_substitutions.txt"; echo pii_substitutions_exit=$?
2. NEXT CONTEXT
The whole list. The six files patched below ride as receipts only; the next car’s file is prompt_foo.py, and foo_files.py rides so the earmarks can be banked.
# --- THE MAC'S FIRST TRAIN: connector roots, breadcrumbs, harness; the stats writer for the next car ---
prompt_foo.py
foo_files.py
! git status --porcelain
! grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
! grep -c 'scripts/confluence.py' connectors/confluence.py; grep -c 'scripts/gmail.py' connectors/gmail.py
! grep -c 'connectors/confluence.py' connectors/confluence.py; grep -c 'connectors/gmail.py' connectors/gmail.py
! env -u PIPULATE_ROOT .venv/bin/python -c "import sys; sys.path.insert(0, 'connectors'); import botify, wallet, mcp; print('botify', botify.project_root); print('wallet', wallet.REPO_ROOT); print('mcp', mcp._REPO_ROOT)"
! .venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
! test -f "$HOME/.config/pipulate/pii_substitutions.txt"; echo pii_substitutions_exit=$?
3. PATCHES
No ignition anywhere in this train: the connector words on PATH exec the file at call time, and every probe loads its file fresh. One caveat for this machine: m asks scripts/ai.py for a commit message, and if the Mac has no model to ask, it aborts with “empty message.” The fallback is git add -A && git commit -m "fix(connectors): repo-root arithmetic and breadcrumbs after the move; harness client path" && git push.
Car 0: clean the tree the compiler dirtied, then catch this checkout up. Two actuators, in ~/npvg.
git restore foo_files.py
git pull --ff-only
Car 1: the two breadcrumb sets, one literal replacement, portable across BSD and GNU. It prints its own count before writing; expect confluence 11 and gmail 8.
.venv/bin/python - <<'EOF'
from pathlib import Path
for name in ("confluence", "gmail"):
p = Path("connectors", f"{name}.py")
s = p.read_text(encoding="utf-8")
old, new = f"scripts/{name}.py", f"connectors/{name}.py"
print(name, s.count(old), "->", new)
p.write_text(s.replace(old, new), encoding="utf-8")
EOF
Car 2: three roots, one level too high.
Target: connectors/botify.py
[[[SEARCH]]]
# NOTE: connectors/ is one level deeper than scripts/, hence three parents.
[[[DIVIDER]]]
# NOTE: connectors/ sits at the repo root, beside scripts/, since the move
# out of scripts/connectors/ -- hence TWO parents. Three reached $HOME, read
# off the first Mac compile after the move; the import survived only because
# the editable install also exposes `config`.
[[[REPLACE]]]
Target: connectors/botify.py
[[[SEARCH]]]
project_root = Path(__file__).resolve().parent.parent.parent
[[[DIVIDER]]]
project_root = Path(__file__).resolve().parent.parent
[[[REPLACE]]]
Target: connectors/wallet.py
[[[SEARCH]]]
# back to its own parent.parent; wallet.py lives one level deeper
# (connectors/), so parents[2] is the repo root. Keep in sync.
[[[DIVIDER]]]
# back to its own parent.parent; wallet.py lives at the same depth, in
# connectors/ beside scripts/, so parents[1] is the repo root. parents[2]
# reached $HOME after the move out of scripts/connectors/ and was masked
# inside the shell by PIPULATE_ROOT. Keep in sync.
[[[REPLACE]]]
Target: connectors/wallet.py
[[[SEARCH]]]
REPO_ROOT = Path(os.environ.get('PIPULATE_ROOT') or Path(__file__).resolve().parents[2])
[[[DIVIDER]]]
REPO_ROOT = Path(os.environ.get('PIPULATE_ROOT') or Path(__file__).resolve().parents[1])
[[[REPLACE]]]
Target: connectors/mcp.py
[[[SEARCH]]]
_REPO_ROOT = Path(__file__).resolve().parent.parent.parent
[[[DIVIDER]]]
# Two parents since connectors/ moved to the repo root. Three reached $HOME
# and parked every FDR receipt under the home directory's browser_cache
# instead of the repo's gitignored one (INFERRED from the first Mac compile
# after the move; the root probe is the witness).
_REPO_ROOT = Path(__file__).resolve().parent.parent
[[[REPLACE]]]
Car 3: the harness finds its instrument again.
Target: scripts/mcp_dummy_server.py
[[[SEARCH]]]
CLIENT = Path(__file__).resolve().parent / "connectors" / "mcp.py"
[[[DIVIDER]]]
# scripts/connectors/mcp.py no longer exists: the client moved to the repo
# root's connectors/, one level up from this file's own folder. Until this
# line moved with it, --selftest opened with "fault harness ABORT: client
# not found" and every flight card since the move was a non-reading.
CLIENT = Path(__file__).resolve().parent.parent / "connectors" / "mcp.py"
[[[REPLACE]]]
4. PROMPT
The Mac's first train landed; the receipts above are its AFTER (three roots ending in npvg, the harness verdict line, the two grep pairs, the apply.py count after the pull). This turn is prompt_foo.py's stats-block writer, convicted by the blackout in two ways the Telemetry of the last compile shows verbatim.
Car 1, prompt_foo.py, the stats-block refresh. (a) It rewrites a TRACKED file on every compile, cpr included, so a newcomer's first cpr dirties foo_files.py and their next nix develop prints "Local modifications detected. Skipping automatic update" about work they never did; on the Mac the block reads "0 articles at Mac Shadow" against HEAD's 1,490. Choose the gate with the code in front of you: skip the rewrite when the target-1 corpus is empty, or when PIPULATE_BLOGS_CONFIG names the shadow config, or move the block to an untracked file the compiler reads in; say which and why. (b) On a failed Honeybot fetch it wrote "Honeybot telemetry fetched <stamp>" beside blank DOM-hydration fields and dropped the Markdown negotiated line: keep the previous block verbatim and write "unreachable at <stamp>" instead. AFTER: git status --porcelain reads empty after a compile on the Mac; the stats lines in foo_files.py still read 1,490 and the 2026-09-20T17:42Z stamp.
Car 2, foo_files.py, TODO lines to bank, one each: the 2nd brain is a git repo, so the blackout hedge is a clone at ~/repos/trimnoir on the Mac (rgx's own fallback path, zero config) plus a read-only target in blogs.shadow.json for posts -t, never a website; pii_substitutions.txt is MISSING on the Mac after rm -rf ~/.config/pipulate, so a connector receipt compiled there rides unscrubbed until scrub recreates it; the move's sed could not see scripts/confluence.py or scripts/gmail.py because they never said scripts/connectors/, so a census (rg -n 'scripts/[a-z_]+\.py' connectors/) precedes any future move; sources_menu.py's docstring still says the door tells the human to type `sources` where the word is now conn.
Do not touch the connectors again this turn; the receipts are their AFTER.
5. EXTERNAL DELIVERABLES
None this turn outside the repo. When the Linux box wakes: git clone trimnoir to ~/repos/trimnoir on the Mac, by hand, over the LAN.
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:
(nix:nix-shell-env) (nix) npvg $ 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'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: foo_files.py
no changes added to commit (use "git add" and/or "git commit -a")
(nix:nix-shell-env) (nix) npvg $ blast
📝 Committing: chore(router): set AI-edit blast boundary (foo_files.py context curation)
[main 18a61d8] chore(router): set AI-edit blast boundary (foo_files.py context curation)
1 file changed, 5 insertions(+), 5 deletions(-)
🚀 Pushing 1 commit(s) to remote...
To github.com:pipulate/pipulate.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'github.com:pipulate/pipulate.git'
hint: Updates were rejected because the remote contains work that you do not
hint: have locally. This is usually caused by another repository pushing to
hint: the same ref. If you want to integrate the remote changes, use
hint: 'git pull' before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
(nix:nix-shell-env) (nix) npvg $
Interesting! I have to make the decision now whether I really want to start
doing development work on the Mac now and deal with merging later, though it
appears it’s only the apply.py file that’s still ahead on my not-yet-pushed
blackout cut-off repo, but it’s pretty amazing that such a
attempt-at-showblocking by fate had its blast radius damage constrained only to
that. This is generally an all-around success. Where things have gone out of
whack are both profoundly visible as you can hear from the LLM’s response and
incredibly small divergence which I can just re-apply as a patch to apply.py
from right out of that past article again. In other words I can safely and with
confidence do this:
(nix:nix-shell-env) (nix) npvg $ git push --force
Enumerating objects: 392, done.
Counting objects: 100% (392/392), done.
Delta compression using up to 8 threads
Compressing objects: 100% (337/337), done.
Writing objects: 100% (392/392), 3.24 MiB | 1.50 MiB/s, done.
Total 392 (delta 28), reused 388 (delta 26), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (28/28), done.
To github.com:pipulate/pipulate.git
+ e3a9cd7...18a61d8 main -> main (forced update)
(nix:nix-shell-env) (nix) npvg $
…which clears the way to forge ahead with the 5-Car Train from the Mac which is the truly empowering thing. This is best of Cloud and best of Local-first Hybrid. Can’t stop me not even with a through-the-night blackout that attempted to throw a monkey-wrench into the best laid plans but instead we can start ratcheting up new no take-back wins in the Forever Machine.
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:nix-shell-env) (nix) npvg $ git status --porcelain
grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
grep -c 'scripts/confluence.py' connectors/confluence.py; grep -c 'scripts/gmail.py' connectors/gmail.py
grep -c 'connectors/confluence.py' connectors/confluence.py; grep -c 'connectors/gmail.py' connectors/gmail.py
env -u PIPULATE_ROOT .venv/bin/python -c "import sys; sys.path.insert(0, 'connectors'); import botify, wallet, mcp; print('botify', botify.project_root); print('wallet', wallet.REPO_ROOT); print('mcp', mcp._REPO_ROOT)"
.venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
test -f "$HOME/.config/pipulate/pii_substitutions.txt"; echo pii_substitutions_exit=$?
0
11
8
0
2
botify /Users/michaellevin
wallet /Users/michaellevin
mcp /Users/michaellevin
fault harness ABORT: client not found at /Users/michaellevin/npvg/scripts/connectors/mcp.py
pii_substitutions_exit=1
(nix:nix-shell-env) (nix) npvg $
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 _ _ _ ____ _ _ ___ ____ _
# / \ __| | | | | | ___ ___ / ___| | | |/ _ \| _ \| | Stop! Hammer time! U Can't Touch This. U Can't Stop This. Take that, Fate! Oh, is that what you call tempting Fate?
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | | | |_| | | | | |_) | |
# ahc ___ \ (_| | | _ | (_) | (__ | |___| _ | |_| | __/|_|
# /_/ \_\__,_| |_| |_|\___/ \___| \____|_| |_|\___/|_| (_)
# To Human: WELCOME TO VIM! It's really NeoVim but I say vim anyway.
# To AI: This is an alternate 40K view to the big book-ore rolling pin spine.
# 1. **Probe**: Baseline Reading
# 2. **Context**: Post-experiment *planned* reading instructions
# 3. **Patch**: The experiment and how to make it happen
# 4. **Prompt**: Post-experiment instructions and how to read results
# 5. **Deliverable**: How the world is forever different moving forward
# The first thing you need to know here is that everything that comes after the
# hash symbol (#) is commented out — and that's EVERYTHING in this file's default
# state. Begin editing-in lines for inclusion as part of the context or adding
# chunks of new context at the bottom. `Ctrl`+`v`, `j` (repeatedly), `l` (to move
# right), `d` (to delete). Reverse that with `Ctrl`+`v`, `j` (repeatedly),
# `Shift`+`i`, `# `, `Esc` to put the hashes back. You can just arrow-key around
# here with `h`, `j`, `k`, `l`. Save-and-quit is a bit tricky because another
# file is also loaded: `Esc`, `:`, `q`, `w`, `!`
# If this is stressing you out and you're a quitter and want to quit, just type:
# `Esc`, `:`, `q`, `!`, `Enter`. That will exit without saving any changes. If
# you want to get over this hump, type: `Esc`, `:`, `T`, `u`, `t`, `o`, `r`, `Enter`.
# This file is just to make it easy having options of what to edit into context.
# You can use whatever text-file you want to stack file-names and commands to
# build an output text-file with the identically stacked output of each file or
# command. In this way we vertically append or "stack" a bunch of text; simple as
# that. If you understand this concept, you're on your way to future-proofing
# yourself in the Age of AI. Congratulations! Here is how to include web pages:
# !URL --------------------------------------------------------------------
# when Public page; what a stranger or crawler sees; the BEFORE of a
# login-wall diagnosis
# switch It shows a login page -> `warm URL` once, then `?URL`
#
# ?URL --------------------------------------------------------------------
# when Anything behind a login, on the site's persistent profile;
# `check URL` first
# switch The lenses show a shell (nav, an `[Iframe]` leaf, no content) ->
# read the wire truth for the XHR the frame makes, then call that
# API with a connector
#
# @URL --------------------------------------------------------------------
# when Every re-read of a page already scraped; no browser, no network
# switch The cached page is stale or was a login wall -> fresh `!` or `?`
#
# $URL --------------------------------------------------------------------
# when Exact markup: meta tags, a JSON blob in a `<script>`
# note Token-heavy; needs a prior scrape
#
# %URL --------------------------------------------------------------------
# when The network log distilled; SPA endpoint discovery
# switch It re-serves the wire truth you already have -> the API
#
# ! cmd -------------------------------------------------------------------
# when Any bounded, non-interactive command as a live receipt
# note Cap it with `-n`; no aliases, no prompts
#
# Connector ---------------------------------------------------------------
# when The number you want is one GET away
# switch LIST until the thing isn't in the list -> FETCH by id -> DRILL
# the path the app's own frame called -> `--grep` to narrow a list
# or find a leaf
# Every step is one argument longer than the last; the moment a lens shows less than the wire does is the moment to stop scraping.
# FOR 40K-FT VIEW (STORY & INFRASTRUCTURE) !!
# --- START EDITING-IN ON 1ST TURN ---
# ! python scripts/articles/lsa.py -t 1 --reverse --fmt dated-slugs # <-- ROLLING PIN that gives the 40K foot book-spine view of book-ore (only works for me because of local-only git repo)
# ~/repos/nixos/autognome.py # <-- You wake up in the morning and your Tooling & Instrumentation folds out of you like Inspector Gadget.
# init.lua # <-- Those gadgets are made easy-to-use through nifty keyboard shortcuts (but ya gotta learn vim).
# GLOSSARY.md # <-- Like the back of a J.R.R. Tolkien book, there's kooky new terms to know.
# flake.nix # <-- Here is my hardware. Here is my state. Put on your sandbox. And please recreate. (Infrastructure as Code / IaC)
# 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)
# scripts/sources_menu.py # <-- what `conn` prints.
# connectors/README.md # <-- the contract, the wallet, and the six auth kinds every new connector copies one of
# connectors/wallet.py # <-- Connect your accounts, or any site by URL; see what's live.
# connectors/botify.py # <-- Bring Botify crawl data and BQL query results into context.
# connectors/confluence.py # <-- Bring a Confluence space, page, or search hit into context.
# connectors/gmail.py # <-- Bring an email thread or a sender's threads into context.
# connectors/gsc.py # <-- Bring Search Console properties or top queries into context.
# connectors/jira.py # <-- List your open Jira tickets, or fetch one by key.
# connectors/sheets.py # <-- Bring a Google Sheet's tabs and cell data into context.
# connectors/slack.py # <-- Bring a Slack channel or message thread into context.
# connectors/mcp.py # <-- Replay client for remote MCP servers (Streamable HTTP transport).
# connectors/mcp_warm.py # <-- Mint the OAuth bearer token a remote MCP server asks for.
# connectors/noop.py # <-- The honest non-operative connector: one positional, prints what it received, exits 0; what public_walk.yaml names at every stop, because a plan must name something that RUNS
# scripts/mcp_dummy_server.py # <-- The fault harness behind mcp.py (20/20 against the unmodified client); it shares mcp.py's spec reading, so its agreement is a tautology, never a vendor witness
# Context 2
# --- THE MAC'S FIRST TRAIN: connector roots, breadcrumbs, harness; the stats writer for the next car ---
prompt_foo.py
foo_files.py
! git status --porcelain
! grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
! grep -c 'scripts/confluence.py' connectors/confluence.py; grep -c 'scripts/gmail.py' connectors/gmail.py
! grep -c 'connectors/confluence.py' connectors/confluence.py; grep -c 'connectors/gmail.py' connectors/gmail.py
! env -u PIPULATE_ROOT .venv/bin/python -c "import sys; sys.path.insert(0, 'connectors'); import botify, wallet, mcp; print('botify', botify.project_root); print('wallet', wallet.REPO_ROOT); print('mcp', mcp._REPO_ROOT)"
! .venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
! test -f "$HOME/.config/pipulate/pii_substitutions.txt"; echo pii_substitutions_exit=$?
The fact that this is now so easy peasy possible on the Mac bodes very well for this system and the much simpler version of this that is the coming “QA My dot AI” site and process. I didn’t get up to activating my new qamy.ai domain this weekend but the momentum is there. That’s really what this article is about: preservation of momentum using as many tricks to become unstoppable as there are tricks tying to turn the otherwise potentially very technically powerful citizenry if properly educated into cloud-dependent consumers through the withholding of proper tech education.
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:nix-shell-env) (nix) npvg $ git restore foo_files.py
git pull --ff-only
Already up to date.
(nix:nix-shell-env) (nix) npvg $ d
(nix:nix-shell-env) (nix) npvg $ m
❌ ai.py returned empty message, aborting.
(nix:nix-shell-env) (nix) npvg $ .venv/bin/python - <<'EOF'
from pathlib import Path
for name in ("confluence", "gmail"):
p = Path("connectors", f"{name}.py")
s = p.read_text(encoding="utf-8")
old, new = f"scripts/{name}.py", f"connectors/{name}.py"
print(name, s.count(old), "->", new)
p.write_text(s.replace(old, new), encoding="utf-8")
EOF
confluence 11 -> connectors/confluence.py
gmail 8 -> connectors/gmail.py
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/connectors/confluence.py b/connectors/confluence.py
index 3154ac2..2b23ef5 100644
--- a/connectors/confluence.py
+++ b/connectors/confluence.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# scripts/confluence.py
+# connectors/confluence.py
"""
confluence.py — Bring a Confluence space, page, or search hit into context.
@@ -7,16 +7,16 @@ A Unix-philosophy gateway to the Confluence API for Prompt Fu context.
Golden-path modes, auto-detected from the single positional argument:
- python scripts/confluence.py # LIST: all spaces you can see
- python scripts/confluence.py SPACEKEY # LIST: recently modified pages in that space
- python scripts/confluence.py 123456789 # FETCH: full page text by numeric page ID
- python scripts/confluence.py 'search words' # SEARCH: CQL text search across pages
+ python connectors/confluence.py # LIST: all spaces you can see
+ python connectors/confluence.py SPACEKEY # LIST: recently modified pages in that space
+ python connectors/confluence.py 123456789 # FETCH: full page text by numeric page ID
+ python connectors/confluence.py 'search words' # SEARCH: CQL text search across pages
Designed to be dropped into adhoc.txt as a `!` chisel-strike, e.g.:
- ! python scripts/confluence.py
- ! python scripts/confluence.py ENG
- ! python scripts/confluence.py 123456789
+ ! python connectors/confluence.py
+ ! python connectors/confluence.py ENG
+ ! python connectors/confluence.py 123456789
Disambiguation rule: an all-digit argument is a page ID (FETCH); an argument
containing whitespace is a search (CQL text query); any other single token is
@@ -114,7 +114,7 @@ def list_spaces(client, base, max_items):
return
for s in results[:max_items]:
print(f"{s.get('key', '?')} {s.get('name', '')}")
- print("\n# Next: python scripts/confluence.py <SPACEKEY> (list recent pages)")
+ print("\n# Next: python connectors/confluence.py <SPACEKEY> (list recent pages)")
def list_space_pages(client, base, space_key, max_items):
@@ -129,7 +129,7 @@ def list_space_pages(client, base, space_key, max_items):
return
for p in results[:max_items]:
print(f"{p.get('id', '?')} {p.get('title', '')}")
- print("\n# Next: python scripts/confluence.py <page_id> (fetch full page text)")
+ print("\n# Next: python connectors/confluence.py <page_id> (fetch full page text)")
def fetch_page(client, base, page_id):
@@ -158,7 +158,7 @@ def search_pages(client, base, text, max_items):
return
for p in results[:max_items]:
print(f"{p.get('id', '?')} {p.get('title', '')}")
- print("\n# Next: python scripts/confluence.py <page_id> (fetch full page text)")
+ print("\n# Next: python connectors/confluence.py <page_id> (fetch full page text)")
# ----------------------------------------------------------------------------
diff --git a/connectors/gmail.py b/connectors/gmail.py
index e99d389..ab116a0 100644
--- a/connectors/gmail.py
+++ b/connectors/gmail.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
-# scripts/gmail.py
+# connectors/gmail.py
"""
gmail.py — Bring an email thread or a sender's threads into context.
@@ -7,18 +7,18 @@ A Unix-philosophy gateway to the Gmail API for Prompt Fu context.
Two golden-path modes, auto-detected from the single positional argument:
- python scripts/gmail.py user@domain.com # LIST: recent threads involving them
- python scripts/gmail.py <thread_id> # FETCH: full clean transcript of a thread
+ python connectors/gmail.py user@domain.com # LIST: recent threads involving them
+ python connectors/gmail.py <thread_id> # FETCH: full clean transcript of a thread
Designed to be dropped into foo_files.py as a `!` chisel-strike, e.g.:
- ! python scripts/gmail.py michael.levin@botify.com
- ! python scripts/gmail.py 18f4ad923b1c83e2
+ ! python connectors/gmail.py michael.levin@botify.com
+ ! python connectors/gmail.py 18f4ad923b1c83e2
A subject search and a Gmail web thread URL are also accepted:
- python scripts/gmail.py 'SM Store Locator upgrade' # SEARCH by subject
- python scripts/gmail.py 'https://mail.google.com/mail/u/0/#all/<hexId>' # FETCH via URL
+ python connectors/gmail.py 'SM Store Locator upgrade' # SEARCH by subject
+ python connectors/gmail.py 'https://mail.google.com/mail/u/0/#all/<hexId>' # FETCH via URL
Disambiguation rule (checked in this order): an argument starting with http(s)
is a Gmail web URL (FETCH the hex thread id in its fragment; a legacy
@@ -129,7 +129,7 @@ def get_service():
sys.stderr.write(
"Gmail auth needs a one-time interactive login.\n"
"Run this directly in your terminal first to mint the token:\n"
- " python scripts/gmail.py your-email@domain.com\n"
+ " python connectors/gmail.py your-email@domain.com\n"
"After that, the `!` invocation inside foo_files runs silently.\n"
)
sys.exit(1)
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore: Rename `scripts/confluence.py` to `connectors/confluence.py`
[main e47ce2c] chore: Rename `scripts/confluence.py` to `connectors/confluence.py`
2 files changed, 19 insertions(+), 19 deletions(-)
(nix:nix-shell-env) (nix) npvg $ patch
(nix:nix-shell-env) (nix) npvg $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'connectors/botify.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'connectors/botify.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'connectors/wallet.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'connectors/wallet.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'connectors/mcp.py'.
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/connectors/botify.py b/connectors/botify.py
index 61f2f21..784dbc1 100644
--- a/connectors/botify.py
+++ b/connectors/botify.py
@@ -57,10 +57,13 @@ from datetime import datetime, timedelta, timezone
import httpx
# Wire into the central config (same pattern as scripts/ai.py).
-# NOTE: connectors/ is one level deeper than scripts/, hence three parents.
+# NOTE: connectors/ sits at the repo root, beside scripts/, since the move
+# out of scripts/connectors/ -- hence TWO parents. Three reached $HOME, read
+# off the first Mac compile after the move; the import survived only because
+# the editable install also exposes `config`.
# The editable install also exposes `config`, but the explicit path keeps
# this file honest as a standalone, curl-able artifact.
-project_root = Path(__file__).resolve().parent.parent.parent
+project_root = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(project_root))
from config import get_botify_token
diff --git a/connectors/mcp.py b/connectors/mcp.py
index 7ba804a..32c349e 100644
--- a/connectors/mcp.py
+++ b/connectors/mcp.py
@@ -87,7 +87,11 @@ TIMEOUT = 30.0
# territory, same as every other capture lane in this repo.
# ---------------------------------------------------------------------------
RECEIPT_FRAME = "mcp-receipt-v1"
-_REPO_ROOT = Path(__file__).resolve().parent.parent.parent
+# Two parents since connectors/ moved to the repo root. Three reached $HOME
+# and parked every FDR receipt under the home directory's browser_cache
+# instead of the repo's gitignored one (INFERRED from the first Mac compile
+# after the move; the root probe is the witness).
+_REPO_ROOT = Path(__file__).resolve().parent.parent
_EXCHANGES = []
_RECEIPT_META = {}
diff --git a/connectors/wallet.py b/connectors/wallet.py
index 9588794..6a055a6 100644
--- a/connectors/wallet.py
+++ b/connectors/wallet.py
@@ -104,9 +104,11 @@ DOTENV_PATH = Path(os.environ.get('PIPULATE_DOTENV') or
# Repo root anchors browser_session profiles (data/uc_profiles/<name>), the
# SAME directory weblogin.py writes. weblogin honors PIPULATE_ROOT then falls
-# back to its own parent.parent; wallet.py lives one level deeper
-# (connectors/), so parents[2] is the repo root. Keep in sync.
-REPO_ROOT = Path(os.environ.get('PIPULATE_ROOT') or Path(__file__).resolve().parents[2])
+# back to its own parent.parent; wallet.py lives at the same depth, in
+# connectors/ beside scripts/, so parents[1] is the repo root. parents[2]
+# reached $HOME after the move out of scripts/connectors/ and was masked
+# inside the shell by PIPULATE_ROOT. Keep in sync.
+REPO_ROOT = Path(os.environ.get('PIPULATE_ROOT') or Path(__file__).resolve().parents[1])
# Auth kinds — these strings MUST match connectors.json exactly.
_OAUTH_KIND = 'oauth_token_file' # mint + auto-refresh (gmail, sheets)
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore: Update repo root paths in connectors
[main a2548dd] chore: Update repo root paths in connectors
3 files changed, 15 insertions(+), 6 deletions(-)
(nix:nix-shell-env) (nix) npvg $ git push
Enumerating objects: 18, done.
Counting objects: 100% (18/18), done.
Delta compression using up to 8 threads
Compressing objects: 100% (11/11), done.
Writing objects: 100% (11/11), 1.69 KiB | 1.69 MiB/s, done.
Total 11 (delta 9), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (9/9), completed with 7 local objects.
To github.com:pipulate/pipulate.git
18a61d8..a2548dd main -> main
(nix:nix-shell-env) (nix) npvg $
See?! The local model is here on the Mac too. It’s a blatant lie that just your average 16GB M2 circa 2022 Mac can’t run local AI for tasks like this just fine. It can. This is just the plain old Ollama install with the old Gemma 3 model. That’s a sweet spot by the way — extremely good for general tasks like writing git commits.
So what this article is here is me using an Apple iPhone to jumpstart the article with Simplenote and then using an old Macbook to continue the work all still while in bed during a blackout using a WiFi Hotspot on my fully charged phone for my fully charged laptop. All I can’t really do is the final publishing of the article, but this is exactly what Linus Torvalds used to talk about, still being able to edit code at 40 thousand feet. Of course nearly all those airplanes have WiFi now, but that’s the point.
- You prevent your skills from atrophying by doing things down at the slightly lower “plumbing” of tech, the fabric of tech that everyone actually uses underneath like git.
- By doing this you not only have deeper and sharper skills but you can use those skills to hedge your bets against the trials and tribulations of fate much better. It’s not survivalist skills. It’s just enough skills.
4: Prompt: The Mac’s first train landed; the receipts above are its AFTER (three roots ending in npvg, the harness verdict line, the two grep pairs, the apply.py count after the pull). This turn is prompt_foo.py’s stats-block writer, convicted by the blackout in two ways the Telemetry of the last compile shows verbatim.
Car 1, prompt_foo.py, the stats-block refresh. (a) It rewrites a TRACKED file on every compile, cpr included, so a newcomer’s first cpr dirties foo_files.py and their next nix develop prints “Local modifications detected. Skipping automatic update” about work they never did; on the Mac the block reads “0 articles at Mac Shadow” against HEAD’s 1,490. Choose the gate with the code in front of you: skip the rewrite when the target-1 corpus is empty, or when PIPULATE_BLOGS_CONFIG names the shadow config, or move the block to an untracked file the compiler reads in; say which and why. (b) On a failed Honeybot fetch it wrote “Honeybot telemetry fetched
Car 2, foo_files.py, TODO lines to bank, one each: the 2nd brain is a git repo, so the blackout hedge is a clone at ~/repos/trimnoir on the Mac (rgx’s own fallback path, zero config) plus a read-only target in blogs.shadow.json for posts -t, never a website; pii_substitutions.txt is MISSING on the Mac after rm -rf ~/.config/pipulate, so a connector receipt compiled there rides unscrubbed until scrub recreates it; the move’s sed could not see scripts/confluence.py or scripts/gmail.py because they never said scripts/connectors/, so a census (rg -n ‘scripts/[a-z_]+.py’ connectors/) precedes any future move; sources_menu.py’s docstring still says the door tells the human to type sources where the word is now conn.
Do not touch the connectors again this turn; the receipts are their AFTER.
5: Deliverables: The ability to continue producing deliverables come Hell or
High Water. Oh and really explain what happened with those effin keys on the
Mac. Why in the world am I pasting with command + shift + V on the Mac to
keep this from turning into file-attachment prompt instead of Ctrl + Shift +
V like on every other platform even thought he Mac totally has a control key
right there I can see it and it works the way one would expect in Vim and
NeoVim? And I don’t even know if I’m saying that right because I always get the
command and option keys mixed up. Did I use the right one here in my
wording? I don’t remember because it was my muscle memory and not my language
center that did the motion. What does all this say about what? About Apple?
About the Developer community and about Vim? Do you see the parallels to
Microsoft having to include WSL? It is now 4:00 AM.
Fable 5.1 Extra: Context verified for the cars below, with one file I owe and own: scripts/mcp_dummy_server.py. Its car never rode last turn (the receipt still reads ABORT), and I listed it as “receipts only,” so it is not here to patch. That is NEXT CONTEXT IS THE WHOLE LIST failing in a way the first conviction did not name: a car that does not land needs its file again. It rides below as a gated actuator instead, and the file is in the next list.
How we did
Five of seven readings landed as predicted. Breadcrumbs 11, 8 to 0, 0 and 0, 2 to 11, 10; three roots ending in npvg; the scrub table still exit 1. Two readings moved for reasons no patch explains, and both are your hands between taps (THE OPERATOR IS A VARIABLE):
git status --porcelain reads clean, not ` M foo_files.py, because blast committed the shadow zeros as 18a61d8 under the router-churn hint's label "context curation," and this compile rewrote the block byte-identically (same cached stamp, new_content == content`, no write). The zeros are not a dirty file now. They are HEAD, and after the force push they are origin: a stranger who installs this morning gets a router that says “0 already-written articles about this repo at Mac Shadow.” That is the 2026-09-16 shadow TODO’s own last sentence coming true, and it is repaired below.
grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py reads 0 after “Already up to date,” which is the third world my prediction did not name: origin was rewritten. ~/npvg sat at the v2.60 release; the Linux box pushed e3a9cd7e Sunday night (your workbench pulled exactly that at 2 AM); the push from ~/npvg was refused for that reason; --force replaced it. Everything between the release and e3a9cd7e is now off origin. It is not lost: it lives on Pipulate Prime, which is dark, and in ~/repos/pipulate on this Mac, which pulled it before the force. The apply.py BANK car is the one commit I can see in that range (the 0 proves it is gone), and it re-lands from the article below, carried rather than cited. Anything else in that range, a probe lists by hash tonight, and THE REWRITE TAX names the remedy for Prime when it wakes: fetch, read the list, cherry-pick what is not already back, then reset hard. The flake’s ff-only pull will refuse until then.
The stats writer, with the code in front of me
Three mechanisms, all in prompt_foo.py, and the first corrects my own overclaim. I said every newcomer’s first cpr dirties foo_files.py; it is every Mac newcomer. On Linux with no blogs.json, target 1 falls to ~/repos/trimnoir/_posts, which does not exist, and update_stats_in_place returns before writing. On a Mac the flake’s Darwin branch writes blogs.shadow.json and creates the empty shadow corpus, so target 1 exists with zero posts and the writer fires. The gate is the count: a corpus with no dated post has no reading to offer, and the committed block, written by the machine that holds the corpus, stands. Placed before the Honeybot call, so a Mac never opens an ssh to a host it does not have.
The blank hydration line has a precise cause. With the host dark, ssh exits 255 and prints nothing; awk’s END{print n "|" t} still prints, with both variables unset, so the pipeline emits a bare | and exits 0 because awk is last and there is no pipefail. if result.returncode == 0 and value accepted | as a value, ok read True, and the writer stamped “fetched.” The cheapest falsifier is the cache file itself, which should read "hydration": "|" and "ok": true; it is probe five. A reading has a shape, digits|digits, and the writer now demands it.
The third is the negative cache eating the last real reading: on a failed pull the writer stored {ok: False, metrics: {}} over the good entry, then returned the good entry once; the next compile within the TTL read the empty cache and dropped the Honeybot lines. The retry clock now advances on every attempt while the reading and its stamp survive a failure, and the console says “Honeybot unreachable; the stats block keeps its
The keys
Your second wording was right. On a Mac, Chrome and Firefox bind paste-without-formatting to Cmd-Shift-V, which is what keeps a big paste from being promoted to an attachment; Safari and most native apps spell the same idea Option-Shift-Cmd-V, “Paste and Match Style.” Option-Shift-V on its own types ◊, so the first note’s wording is not what the hand pressed. The fingers knew; the language center guessed.
Why Cmd and not the Control key sitting right there: the Mac has two modifier layers because it was two computers. Command is from 1984 and owns the application layer, every menu shortcut, so the PC’s Ctrl-X/C/V/Z/S are the Mac’s Cmd-X/C/V/Z/S one-for-one. Control arrived later, with the late-1980s extended keyboards, and when Mac OS X shipped in 2001 as a BSD Unix, Control became exactly the Unix control key and nothing else. Terminal.app does not steal it: Ctrl-C is SIGINT, Ctrl-D is EOF, and in vim Ctrl-V is blockwise visual and Ctrl-W is windows, because the application layer lives on a different key and never contends. The PC never had the second key: Ctrl is both the GUI modifier and the Unix modifier, so every Linux terminal moved copy and paste to Ctrl-Shift-C and Ctrl-Shift-V to get out of SIGINT’s way, which is where your hands learned them, and Windows Terminal defaults Ctrl-V to paste, which is why vim’s block mode is broken there until you change a binding. The chord you cannot verbalize is on the one platform where the two layers do not fight, and the 1-to-1 mapping your language center wants, Ctrl = Control, is the one mapping that would have broken vim.
So the assay you named is the right one: a platform is a developer platform when vim’s Control chords reach the editor untouched. The Mac passes because of 2001, when Apple became Unix rather than emulating it; Windows passes only inside WSL, since 2016, for the same reason and fifteen years later. The Mac’s key split is the fossil of its own WSL moment, and vim is the litmus paper because it predates every GUI modifier and asks only that Control mean Control. Apple did not design that; it inherited a key it had never used for GUI shortcuts and grafted Unix onto it (THE EXAPTATION RULE, with a modifier key as the crystallin).
1. PROBES
From ~/npvg. Line one reads empty now and empty after, and the sameness is the discriminator this time: without the gate, the next compile rewrites the restored block to zeros and it reads ` M foo_files.py. Line two, 0 to 1, the BANK car re-landed. Line three, 0 to 1, the stats block restored. Line four, 0 and 0 to 2 and 1. Line five is a CENSUS that convicts the awk mechanism: “hydration”: “ |
” beside “ok”: true, both taps. Line six reads empty now (the workbench's origin/main is still e3a9cd7e) and lists the orphaned commits after Car 0's fetch. Line seven is a CENSUS of the workbench's dirty files, names only. Line eight reads the ABORT sentence now and # 20/20 checks passed` after. |
git status --porcelain
grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
grep -c '^# There are 1,490 already-written' foo_files.py
grep -c '_HONEYBOT_SHAPE' prompt_foo.py; grep -c 'Stats block untouched' prompt_foo.py
cat "$HOME/.config/pipulate/honeybot_stats.json"
git -C "$HOME/repos/pipulate" log --oneline origin/main..HEAD | head -12
git -C "$HOME/repos/pipulate" status --porcelain | head -5
.venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
2. NEXT CONTEXT
The whole list, including the file whose car did not ride.
# --- THE ZERO GATE AND THE ORPHANS: the stats writer's AFTER, the harness re-ridden, the rewrite's list ---
prompt_foo.py
foo_files.py
scripts/mcp_dummy_server.py
! git status --porcelain
! grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
! grep -c '^# There are 1,490 already-written' foo_files.py
! grep -c '_HONEYBOT_SHAPE' prompt_foo.py; grep -c 'Stats block untouched' prompt_foo.py
! cat "$HOME/.config/pipulate/honeybot_stats.json"
! git -C "$HOME/repos/pipulate" log --oneline origin/main..HEAD | head -12
! git -C "$HOME/repos/pipulate" status --porcelain | head -5
! .venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
3. PATCHES
No ignition required: the next compile runs the patched prompt_foo.py itself, and every other probe reads its file at call time.
Car 0: let the workbench see what the rewrite orphaned. One actuator, refs only, worktree untouched.
git -C "$HOME/repos/pipulate" fetch origin
Car 1: the stats writer, three mechanisms, one commit story. Six blocks.
Target: prompt_foo.py
[[[SEARCH]]]
HONEYBOT_TIMEOUT_SECONDS = 20
[[[DIVIDER]]]
HONEYBOT_TIMEOUT_SECONDS = 20
# A READING HAS A SHAPE (2026-09-21): both awk tails print digits|digits, and
# anything else -- a bare "|" from an END block run over empty input -- is a
# failed pull wearing a value's clothes. Spaces around the bar are tolerated.
_HONEYBOT_SHAPE = re.compile(r'\d+\s*\|\s*\d+(?:\.\d+)?')
[[[REPLACE]]]
Target: prompt_foo.py
[[[SEARCH]]]
now = _time.time()
age = now - cached.get('fetched_epoch', 0)
if cached and 0 <= age < HONEYBOT_TTL_SECONDS:
return cached
[[[DIVIDER]]]
now = _time.time()
# THE RETRY CLOCK IS THE LAST ATTEMPT, NOT THE LAST SUCCESS (2026-09-21).
# A failed pull used to overwrite a real reading with an empty one, so the
# negative cache the next compile honoured carried no metrics and the
# block lost its Honeybot lines inside the TTL. The attempt clock gates
# retries; the fetched clock stamps the reading; a failure advances only
# the first, so the bytes a dark stretch renders are the bytes before it.
since_attempt = now - cached.get('attempted_epoch', cached.get('fetched_epoch', 0))
if cached and 0 <= since_attempt < HONEYBOT_TTL_SECONDS:
return cached
[[[REPLACE]]]
Target: prompt_foo.py
[[[SEARCH]]]
value = result.stdout.strip()
if result.returncode == 0 and value:
metrics[name] = value
[[[DIVIDER]]]
value = result.stdout.strip()
# THE EMPTY PIPE READ AS A READING (convicted 2026-09-21, blackout).
# With the host dark, ssh exits 255 and prints nothing, awk's END
# block still prints n "|" t with both unset, and the pipeline's
# exit is awk's 0 because there is no pipefail. A bare "|" passed
# the truthy test, ok read True, and the router was written as
# "DOM hydration: trapdoor triggers from non-local IPs" under a
# "fetched" stamp. The shape check below is the whole cure.
if result.returncode == 0 and _HONEYBOT_SHAPE.fullmatch(value):
metrics[name] = value
[[[REPLACE]]]
Target: prompt_foo.py
[[[SEARCH]]]
fresh = {
'fetched_epoch': now,
'fetched_at': datetime.fromtimestamp(now, timezone.utc).strftime('%Y-%m-%dT%H:%MZ'),
'ok': bool(metrics),
'metrics': metrics,
}
[[[DIVIDER]]]
if metrics:
fresh = {
'fetched_epoch': now, 'attempted_epoch': now,
'fetched_at': datetime.fromtimestamp(now, timezone.utc).strftime('%Y-%m-%dT%H:%MZ'),
'ok': True, 'metrics': metrics,
}
elif cached.get('metrics'):
# Stale-but-real beats silence: keep the reading and its own stamp,
# advance only the attempt clock, and say so on the console.
fresh = dict(cached)
fresh.update(attempted_epoch=now, ok=False)
logger.print(f"📡 Honeybot unreachable; the stats block keeps its "
f"{cached.get('fetched_at', 'earlier')} reading.")
else:
fresh = {'fetched_epoch': 0, 'attempted_epoch': now, 'fetched_at': None,
'ok': False, 'metrics': {}}
[[[REPLACE]]]
Target: prompt_foo.py
[[[SEARCH]]]
if not fresh['ok'] and cached.get('metrics'):
# Stale-but-real beats silence; the rendered timestamp says how stale.
return cached
return fresh
[[[DIVIDER]]]
return fresh
[[[REPLACE]]]
Target: prompt_foo.py
[[[SEARCH]]]
count = len(posts)
[[[DIVIDER]]]
count = len(posts)
# THE ZERO-ARTICLE GATE (convicted 2026-09-21, blackout, Mac). A Darwin
# shell points target 1 at an empty shadow corpus, so every Mac compile,
# cpr included, rewrote this TRACKED file to read zero articles; the
# next nix develop then refused to auto-update over local modifications
# the human never made, and one blast later the zeros were on origin.
# A corpus with no dated post has no reading to offer, so the committed
# block, written by the machine that holds the corpus, stands. A forker
# with a real blog still gets their own numbers. Placed before the
# Honeybot call on purpose: a Mac never opens an ssh it cannot complete.
if count == 0:
logger.print(f"📊 Stats block untouched: {blog_name} holds no dated posts; the committed reading stands.")
return
[[[REPLACE]]]
Car 2: the router says what the corpus said, not what the shadow said. The five lines are the exact lines git removed on the Mac’s first compile, read off that compile’s Telemetry.
Target: foo_files.py
[[[SEARCH]]]
# There are 0 already-written articles about this repo at Mac Shadow - MikeLev.in
# Velocity: 0 published in the last 7 days
# DOM hydration: trapdoor triggers from non-local IPs (top-N sample, self excluded)
# Honeybot telemetry fetched 2026-09-21T07:23Z
[[[DIVIDER]]]
# There are 1,490 already-written articles about this repo at MikeLev.in (Public)
# Velocity: 26 published in the last 7 days
# Markdown negotiated: 2,655 reads (0.21% of all responses)
# DOM hydration: 2970 trapdoor triggers from 15 non-local IPs (top-N sample, self excluded)
# Honeybot telemetry fetched 2026-09-20T17:42Z
[[[REPLACE]]]
Car 3: the harness car that did not ride, as a gated actuator. It counts the old line before writing and stops on anything but exactly one; the comment car rides next turn with the file in context.
.venv/bin/python - <<'EOF'
from pathlib import Path
p = Path("scripts/mcp_dummy_server.py")
s = p.read_text(encoding="utf-8")
old = 'CLIENT = Path(__file__).resolve().parent / "connectors" / "mcp.py"'
new = ('# The client moved to the repo root with connectors/; two parents reach it (2026-09-21).\n'
'CLIENT = Path(__file__).resolve().parent.parent / "connectors" / "mcp.py"')
n = s.count(old)
print("harness client line:", n)
if n == 1:
p.write_text(s.replace(old, new), encoding="utf-8")
print("GO: replaced")
else:
print("STOP: not exactly one match; nothing written")
EOF
Car 4: the BANK car the rewrite orphaned, carried back from the article.
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]]]
Car 5: the debts, banked one line each, and the shadow TODO closed.
Target: foo_files.py
[[[SEARCH]]]
# #todo #to-do #earmarks
[[[DIVIDER]]]
# #todo #to-do #earmarks
# - TODO (2026-09-21, THE FORCE PUSH IN THE DARK; the rewrite tax collected before sunrise): blast from ~/npvg committed the shadow zeros as 18a61d8 under the router-churn hint's own label, the push was refused because origin held e3a9cd7e from Prime's Sunday-night notary, and git push --force replaced it, so the release..e3a9cd7e range is off origin and lives only on Prime (dark) and in ~/repos/pipulate on the Mac (pulled at 2 AM, before the force). The apply.py BANK car re-landed from the article the same night; whatever else the workbench's origin/main..HEAD lists rides by name. When Prime wakes: git fetch origin, read git log --oneline origin/main..HEAD, cherry-pick what is not already on origin, then git reset --hard origin/main; the flake's ff-only pull refuses until then (THE REWRITE TAX).
# - TODO (2026-09-21, the second brain is a git repo): the blackout cut off ~/repos/trimnoir/_posts and its Jekyll mirror at once, both on dark machines; the hedge is a clone at ~/repos/trimnoir on the Mac (rgx's own fallback path, zero config) plus a read-only target in blogs.shadow.json for posts -t, never a noindexed website, because a website exposes the history the private repo exists to keep. Gate: rgx on the Mac prints filenames.
# - TODO (2026-09-21, the Mac scrubs nothing): pii_substitutions.txt is MISSING on the Mac after rm -rf ~/.config/pipulate (test -f exit 1, deed 0cee0212), so a connector receipt compiled there rides to the chatbot unscrubbed; bare scrub creates the file and lists it. Gate: the identity-scrub line reads N rule(s), not MISSING.
# - TODO (2026-09-21, the sed that could not see): the connectors move's literal sed rewrote scripts/connectors/ and missed confluence.py and gmail.py, which had said scripts/<name>.py since before the folder existed (11 and 8 lines, replaced by a gated python car on the Mac, e47ce2c); before any future move, census rg -n 'scripts/[a-z_]+\.py' <folder>/ and read every spelling, not the one you remember. sources_menu.py's docstring still says the door tells the human to type `sources` where the word is conn; the flake's Darwin branch hardwires the shadow blog's name, so a stranger's Mac console names the author (DE-HARDWIRE THE PERSONAL PATHS, one more site).
[[[REPLACE]]]
Target: foo_files.py
[[[SEARCH]]]
do not fix this by committing the generated zeroes.
[[[DIVIDER]]]
do not fix this by committing the generated zeroes. DISCHARGED 2026-09-21 by the zero-article gate in update_stats_in_place: a corpus with no dated post writes nothing and the committed block stands. Banked one blast too late: the zeros rode 18a61d8 to origin under the router-churn hint before the gate landed, and the block was restored from the 2026-09-20T17:42Z reading in the same train.
[[[REPLACE]]]
4. PROMPT
The stats writer's train landed on the Mac; the receipts above are its AFTER. Read them in this order: git status --porcelain empty proves the zero-article gate held through a compile (the Processing Log carries the "Stats block untouched" line as the second witness); the honeybot_stats.json census should show "hydration": "|" beside "ok": true, the awk END pipe the shape check now refuses; the 1,490 line and the apply.py count both read 1; the harness reads 20/20 or names the check that failed.
The orphan list from ~/repos/pipulate is the one reading nobody predicted a count for. If it lists only the apply.py BANK commit (and a foo_files.py churn commit), nothing further is owed and the workbench and Prime both take git fetch origin then git reset --hard origin/main (stash the workbench's dirty files first; its status --porcelain names them). If it lists anything else, name each hash and its files here and ride them by cherry-pick before either reset.
Then the comment car for scripts/mcp_dummy_server.py's CLIENT line, with the file in context this time, and the operator's call on the dismount: this article's goal was momentum through a blackout, and the receipts that would verify it are a compile on the Mac, a train landed from ~/npvg, the local model writing commit subjects, and a public repo that no longer says zero articles.
5. EXTERNAL DELIVERABLES
Two things live outside this train. The keys explanation above is the article’s deliverable. The other is a hand step on a machine that is dark, carried in full so it is not a bibliography when the power returns:
# ON PIPULATE PRIME, when it wakes, before any blast:
cd ~/repos/pipulate
git fetch origin
git log --oneline origin/main..HEAD
# read that list against the one the Mac probe printed; cherry-pick onto origin/main
# anything not already re-landed (the apply.py BANK car is), then:
git reset --hard origin/main
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:
(nix:nix-shell-env) (nix) npvg $ 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:nix-shell-env) (nix) npvg $ git status --porcelain
grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
grep -c '^# There are 1,490 already-written' foo_files.py
grep -c '_HONEYBOT_SHAPE' prompt_foo.py; grep -c 'Stats block untouched' prompt_foo.py
cat "$HOME/.config/pipulate/honeybot_stats.json"
git -C "$HOME/repos/pipulate" log --oneline origin/main..HEAD | head -12
git -C "$HOME/repos/pipulate" status --porcelain | head -5
.venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
0
0
0
0
{
"fetched_epoch": 1789975422.5370271,
"fetched_at": "2026-09-21T07:23Z",
"ok": true,
"metrics": {
"hydration": "|"
}
} D pipulate/__init__.py
fault harness ABORT: client not found at /Users/michaellevin/npvg/scripts/connectors/mcp.py
(nix:nix-shell-env) (nix) npvg $
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 _ _ _ ____ _ _ ___ ____ _
# / \ __| | | | | | ___ ___ / ___| | | |/ _ \| _ \| | Stop! Hammer time! U Can't Touch This. U Can't Stop This. Take that, Fate! Oh, is that what you call tempting Fate?
# ahe/ _ \ / _` | | |_| |/ _ \ / __| | | | |_| | | | | |_) | | Let's see what Fable 5.1 is doing. Seems like just Mac edge case handling.
# 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)
# scripts/sources_menu.py # <-- what `conn` prints.
# connectors/README.md # <-- the contract, the wallet, and the six auth kinds every new connector copies one of
# connectors/wallet.py # <-- Connect your accounts, or any site by URL; see what's live.
# connectors/botify.py # <-- Bring Botify crawl data and BQL query results into context.
# connectors/confluence.py # <-- Bring a Confluence space, page, or search hit into context.
# connectors/gmail.py # <-- Bring an email thread or a sender's threads into context.
# connectors/gsc.py # <-- Bring Search Console properties or top queries into context.
# connectors/jira.py # <-- List your open Jira tickets, or fetch one by key.
# connectors/sheets.py # <-- Bring a Google Sheet's tabs and cell data into context.
# connectors/slack.py # <-- Bring a Slack channel or message thread into context.
# connectors/mcp.py # <-- Replay client for remote MCP servers (Streamable HTTP transport).
# connectors/mcp_warm.py # <-- Mint the OAuth bearer token a remote MCP server asks for.
# connectors/noop.py # <-- The honest non-operative connector: one positional, prints what it received, exits 0; what public_walk.yaml names at every stop, because a plan must name something that RUNS
# scripts/mcp_dummy_server.py # <-- The fault harness behind mcp.py (20/20 against the unmodified client); it shares mcp.py's spec reading, so its agreement is a tautology, never a vendor witness
# Context 2
# --- THE MAC'S FIRST TRAIN: connector roots, breadcrumbs, harness; the stats writer for the next car ---
# prompt_foo.py
# foo_files.py
# ! git status --porcelain
# ! grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
# ! grep -c 'scripts/confluence.py' connectors/confluence.py; grep -c 'scripts/gmail.py' connectors/gmail.py
# ! grep -c 'connectors/confluence.py' connectors/confluence.py; grep -c 'connectors/gmail.py' connectors/gmail.py
# ! env -u PIPULATE_ROOT .venv/bin/python -c "import sys; sys.path.insert(0, 'connectors'); import botify, wallet, mcp; print('botify', botify.project_root); print('wallet', wallet.REPO_ROOT); print('mcp', mcp._REPO_ROOT)"
# ! .venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
# ! test -f "$HOME/.config/pipulate/pii_substitutions.txt"; echo pii_substitutions_exit=$?
# Context 3
# --- THE ZERO GATE AND THE ORPHANS: the stats writer's AFTER, the harness re-ridden, the rewrite's list ---
prompt_foo.py
foo_files.py
scripts/mcp_dummy_server.py
! git status --porcelain
! grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
! grep -c '^# There are 1,490 already-written' foo_files.py
! grep -c '_HONEYBOT_SHAPE' prompt_foo.py; grep -c 'Stats block untouched' prompt_foo.py
! cat "$HOME/.config/pipulate/honeybot_stats.json"
! git -C "$HOME/repos/pipulate" log --oneline origin/main..HEAD | head -12
! git -C "$HOME/repos/pipulate" status --porcelain | head -5
! .venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line`
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'.
Untracked files:
(use "git add <file>..." to include in what will be committed)
1!
nothing added to commit but untracked files present (use "git add" to track)
(nix:nix-shell-env) (nix) npvg $ git -C "$HOME/repos/pipulate" fetch origin
remote: Enumerating objects: 22, done.
remote: Counting objects: 100% (22/22), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 14 (delta 11), reused 13 (delta 10), pack-reused 0 (from 0)
Unpacking objects: 100% (14/14), 2.07 KiB | 84.00 KiB/s, done.
From github.com:miklevin/pipulate
+ e3a9cd7e...a2548ddb main -> origin/main (forced update)
(nix:nix-shell-env) (nix) npvg $ d
--- UNTRACKED (invisible to the diff above; m will stage these) ---
+ 1!
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore: Update documentation and clarify AI context usage
[main 4e9087c] chore: Update documentation and clarify AI context usage
1 file changed, 158 insertions(+)
create mode 100644 1!
(nix:nix-shell-env) (nix) npvg $ patch
(nix:nix-shell-env) (nix) npvg $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'prompt_foo.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'prompt_foo.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'prompt_foo.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'prompt_foo.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'prompt_foo.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'prompt_foo.py'.
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/prompt_foo.py b/prompt_foo.py
index 14364d6..53c9634 100644
--- a/prompt_foo.py
+++ b/prompt_foo.py
@@ -2240,6 +2240,10 @@ HONEYBOT_DB_PATH = "~/www/mikelev.in/honeybot.db"
HONEYBOT_CACHE_FILE = CONFIG_DIR / "honeybot_stats.json"
HONEYBOT_TTL_SECONDS = 6 * 3600
HONEYBOT_TIMEOUT_SECONDS = 20
+# A READING HAS A SHAPE (2026-09-21): both awk tails print digits|digits, and
+# anything else -- a bare "|" from an END block run over empty input -- is a
+# failed pull wearing a value's clothes. Spaces around the bar are tolerated.
+_HONEYBOT_SHAPE = re.compile(r'\d+\s*\|\s*\d+(?:\.\d+)?')
_HONEYBOT_PIPE = (
f"ssh -o BatchMode=yes -o ConnectTimeout=5 {HONEYBOT_SSH_HOST} "
f"'sqlite3 {HONEYBOT_DB_PATH}'"
@@ -2272,8 +2276,14 @@ def fetch_honeybot_stats() -> dict:
except (OSError, ValueError, TypeError):
cached = {}
now = _time.time()
- age = now - cached.get('fetched_epoch', 0)
- if cached and 0 <= age < HONEYBOT_TTL_SECONDS:
+ # THE RETRY CLOCK IS THE LAST ATTEMPT, NOT THE LAST SUCCESS (2026-09-21).
+ # A failed pull used to overwrite a real reading with an empty one, so the
+ # negative cache the next compile honoured carried no metrics and the
+ # block lost its Honeybot lines inside the TTL. The attempt clock gates
+ # retries; the fetched clock stamps the reading; a failure advances only
+ # the first, so the bytes a dark stretch renders are the bytes before it.
+ since_attempt = now - cached.get('attempted_epoch', cached.get('fetched_epoch', 0))
+ if cached and 0 <= since_attempt < HONEYBOT_TTL_SECONDS:
return cached
metrics = {}
for name, command in HONEYBOT_METRICS.items():
@@ -2284,24 +2294,38 @@ def fetch_honeybot_stats() -> dict:
timeout=HONEYBOT_TIMEOUT_SECONDS,
)
value = result.stdout.strip()
- if result.returncode == 0 and value:
+ # THE EMPTY PIPE READ AS A READING (convicted 2026-09-21, blackout).
+ # With the host dark, ssh exits 255 and prints nothing, awk's END
+ # block still prints n "|" t with both unset, and the pipeline's
+ # exit is awk's 0 because there is no pipefail. A bare "|" passed
+ # the truthy test, ok read True, and the router was written as
+ # "DOM hydration: trapdoor triggers from non-local IPs" under a
+ # "fetched" stamp. The shape check below is the whole cure.
+ if result.returncode == 0 and _HONEYBOT_SHAPE.fullmatch(value):
metrics[name] = value
except Exception:
continue
- fresh = {
- 'fetched_epoch': now,
- 'fetched_at': datetime.fromtimestamp(now, timezone.utc).strftime('%Y-%m-%dT%H:%MZ'),
- 'ok': bool(metrics),
- 'metrics': metrics,
- }
+ if metrics:
+ fresh = {
+ 'fetched_epoch': now, 'attempted_epoch': now,
+ 'fetched_at': datetime.fromtimestamp(now, timezone.utc).strftime('%Y-%m-%dT%H:%MZ'),
+ 'ok': True, 'metrics': metrics,
+ }
+ elif cached.get('metrics'):
+ # Stale-but-real beats silence: keep the reading and its own stamp,
+ # advance only the attempt clock, and say so on the console.
+ fresh = dict(cached)
+ fresh.update(attempted_epoch=now, ok=False)
+ logger.print(f"📡 Honeybot unreachable; the stats block keeps its "
+ f"{cached.get('fetched_at', 'earlier')} reading.")
+ else:
+ fresh = {'fetched_epoch': 0, 'attempted_epoch': now, 'fetched_at': None,
+ 'ok': False, 'metrics': {}}
try:
HONEYBOT_CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
HONEYBOT_CACHE_FILE.write_text(json.dumps(fresh, indent=2), encoding='utf-8')
except OSError:
pass
- if not fresh['ok'] and cached.get('metrics'):
- # Stale-but-real beats silence; the rendered timestamp says how stale.
- return cached
return fresh
def render_honeybot_stat_lines() -> str:
"""Render telemetry as STATS comment lines, or '' on any failure."""
@@ -2356,6 +2380,18 @@ def update_stats_in_place():
if f.endswith('.md') and f[:4].isdigit()
]
count = len(posts)
+ # THE ZERO-ARTICLE GATE (convicted 2026-09-21, blackout, Mac). A Darwin
+ # shell points target 1 at an empty shadow corpus, so every Mac compile,
+ # cpr included, rewrote this TRACKED file to read zero articles; the
+ # next nix develop then refused to auto-update over local modifications
+ # the human never made, and one blast later the zeros were on origin.
+ # A corpus with no dated post has no reading to offer, so the committed
+ # block, written by the machine that holds the corpus, stands. A forker
+ # with a real blog still gets their own numbers. Placed before the
+ # Honeybot call on purpose: a Mac never opens an ssh it cannot complete.
+ if count == 0:
+ logger.print(f"📊 Stats block untouched: {blog_name} holds no dated posts; the committed reading stands.")
+ return
# Velocity gauge: date-prefixed filenames sort as ISO strings,
# so a plain string compare counts the trailing week for free.
from datetime import date, timedelta
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore: Refine Honeybot stat retrieval logic and handling.
[main fe087da] chore: Refine Honeybot stat retrieval logic and handling.
1 file changed, 48 insertions(+), 12 deletions(-)
(nix:nix-shell-env) (nix) npvg $ patch
(nix:nix-shell-env) (nix) npvg $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/foo_files.py b/foo_files.py
index 8315a3b..524a94a 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -120,10 +120,11 @@ AI_PHOOEY_CHOP = r"""#
# This is a real-time book that's already done and always being written.
# --- START STATS ---
-# There are 0 already-written articles about this repo at Mac Shadow - MikeLev.in
-# Velocity: 0 published in the last 7 days
-# DOM hydration: trapdoor triggers from non-local IPs (top-N sample, self excluded)
-# Honeybot telemetry fetched 2026-09-21T07:23Z
+# There are 1,490 already-written articles about this repo at MikeLev.in (Public)
+# Velocity: 26 published in the last 7 days
+# Markdown negotiated: 2,655 reads (0.21% of all responses)
+# DOM hydration: 2970 trapdoor triggers from 15 non-local IPs (top-N sample, self excluded)
+# Honeybot telemetry fetched 2026-09-20T17:42Z
# --- END STATS ---
# --- START RECEIPTS (newest first; cap 20 lines; a line pushed past the cap is deleted, never moved -- git and the rolling pin are the archive) ---
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore(router): set AI-edit blast boundary (foo_files.py)
[main 8c54366] chore(router): set AI-edit blast boundary (foo_files.py)
1 file changed, 5 insertions(+), 4 deletions(-)
(nix:nix-shell-env) (nix) npvg $ .venv/bin/python - <<'EOF'
from pathlib import Path
p = Path("scripts/mcp_dummy_server.py")
s = p.read_text(encoding="utf-8")
old = 'CLIENT = Path(__file__).resolve().parent / "connectors" / "mcp.py"'
new = ('# The client moved to the repo root with connectors/; two parents reach it (2026-09-21).\n'
'CLIENT = Path(__file__).resolve().parent.parent / "connectors" / "mcp.py"')
n = s.count(old)
print("harness client line:", n)
if n == 1:
p.write_text(s.replace(old, new), encoding="utf-8")
print("GO: replaced")
else:
print("STOP: not exactly one match; nothing written")
EOF
harness client line: 1
GO: replaced
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/scripts/mcp_dummy_server.py b/scripts/mcp_dummy_server.py
index 2039892..1a943e8 100644
--- a/scripts/mcp_dummy_server.py
+++ b/scripts/mcp_dummy_server.py
@@ -75,7 +75,8 @@ from pathlib import Path
PROTOCOL_VERSION = "2025-06-18"
SESSION_HEADER = "Mcp-Session-Id"
SERVER_INFO = {"name": "pipulate-mcp-faultharness", "version": "1.0"}
-CLIENT = Path(__file__).resolve().parent / "connectors" / "mcp.py"
+# The client moved to the repo root with connectors/; two parents reach it (2026-09-21).
+CLIENT = Path(__file__).resolve().parent.parent / "connectors" / "mcp.py"
LINE_CAP = 104 # THE PROBE ECONOMY RULE: bounded rows, always
BASE_CONFIG = {
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore: Update client path in mcp_dummy_server.py
[main 41d4e45] chore: Update client path in mcp_dummy_server.py
1 file changed, 2 insertions(+), 1 deletion(-)
(nix:nix-shell-env) (nix) npvg $ patch
(nix:nix-shell-env) (nix) npvg $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'apply.py'.
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/apply.py b/apply.py
index 2c16006..1606794 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:nix-shell-env) (nix) npvg $ m
📝 Committing: chore: Update documentation for apply.py
[main 018937f] chore: Update documentation for apply.py
1 file changed, 16 insertions(+)
(nix:nix-shell-env) (nix) npvg $ patch
(nix:nix-shell-env) (nix) npvg $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/foo_files.py b/foo_files.py
index 524a94a..b9fce05 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -2219,6 +2219,10 @@ MATCHBOOK_CHOP = r"""
! .venv/bin/python -c 'import re;t=open("GLOSSARY.md",encoding="utf-8").read();[print(h,"—",re.sub(r"\s+"," ",p)) for h,p in re.findall(r"^- \*\*([^*]+)\*\* — \*(.+?)\*",t,flags=re.M|re.S)]'
"""
# #todo #to-do #earmarks
+# - TODO (2026-09-21, THE FORCE PUSH IN THE DARK; the rewrite tax collected before sunrise): blast from ~/npvg committed the shadow zeros as 18a61d8 under the router-churn hint's own label, the push was refused because origin held e3a9cd7e from Prime's Sunday-night notary, and git push --force replaced it, so the release..e3a9cd7e range is off origin and lives only on Prime (dark) and in ~/repos/pipulate on the Mac (pulled at 2 AM, before the force). The apply.py BANK car re-landed from the article the same night; whatever else the workbench's origin/main..HEAD lists rides by name. When Prime wakes: git fetch origin, read git log --oneline origin/main..HEAD, cherry-pick what is not already on origin, then git reset --hard origin/main; the flake's ff-only pull refuses until then (THE REWRITE TAX).
+# - TODO (2026-09-21, the second brain is a git repo): the blackout cut off ~/repos/trimnoir/_posts and its Jekyll mirror at once, both on dark machines; the hedge is a clone at ~/repos/trimnoir on the Mac (rgx's own fallback path, zero config) plus a read-only target in blogs.shadow.json for posts -t, never a noindexed website, because a website exposes the history the private repo exists to keep. Gate: rgx on the Mac prints filenames.
+# - TODO (2026-09-21, the Mac scrubs nothing): pii_substitutions.txt is MISSING on the Mac after rm -rf ~/.config/pipulate (test -f exit 1, deed 0cee0212), so a connector receipt compiled there rides to the chatbot unscrubbed; bare scrub creates the file and lists it. Gate: the identity-scrub line reads N rule(s), not MISSING.
+# - TODO (2026-09-21, the sed that could not see): the connectors move's literal sed rewrote scripts/connectors/ and missed confluence.py and gmail.py, which had said scripts/<name>.py since before the folder existed (11 and 8 lines, replaced by a gated python car on the Mac, e47ce2c); before any future move, census rg -n 'scripts/[a-z_]+\.py' <folder>/ and read every spelling, not the one you remember. sources_menu.py's docstring still says the door tells the human to type `sources` where the word is conn; the flake's Darwin branch hardwires the shadow blog's name, so a stranger's Mac console names the author (DE-HARDWIRE THE PERSONAL PATHS, one more site).
# - TODO (2026-09-19, the header that flows back): the live router at ~/.local/state/pipulate/adhoc.txt still carries the sigil table the 2026-09-18 sed retired from the tracked template (that dismount named it: a second copy the sed never reached), and on 2026-09-19 that header was copied back over the template in the working tree -- sigil table 0 -> 35, the chapter-XVIII pointer gone, prompt_foo.py commented out of the default chop -- caught only because a displacement receipt read +29 where +1 was predicted (WALK_CHOP 1990 -> 2019), and discarded by git checkout before it rode a commit whose subject would have said documentation. Retire the table from the live router by hand in ahe (cut from the line ending "Here is how to include web pages:" through the "Every step is one argument longer" line, restore the four-line pointer), or the next sync flows it back. Two copies of one header drift; THE OPERATOR IS A VARIABLE, and this time the variable was a paste. AMENDED the same day, deed 1496: it was not discarded. The operator committed the paste before the BEFORE tap ("the easiest thing"; the parent of 6e4af6ee carries foo_files.py at blob a427fc2e, the pasted header, by the Telemetry index line), so Car 0's git checkout restored nothing and its gate printed TEMPLATE_RESTORED anyway, a false green that read git's status where it should have read the file (the sigil heading's grep -c at 0, or STOP): AN EXIT CODE IS A VERDICT ONLY FOR THE PATH THAT REACHES IT, in a gate this ride wrote. The sigil table now lives in HEAD's template, the chapter-XVIII pointer is gone, and prompt_foo.py is commented out of AI_PHOOEY_CHOP so bare foo compiles the router alone. TWO AUDIENCES, ONE FILE (2026-09-18) is reversed by action and unruled in words: either the template mirrors the live router, and the pointer and the 2026-09-18 sed are the things to retire, or the table leaves both files by hand; until ruled, the two copies will keep flowing into each other.
# - TODO (2026-09-19, rgx's empty branch): rgx jeopardy.wav ffmpg (a typo) and a true empty intersection both print "No matching articles.", the same line in two worlds, THE DISCRIMINATION QUESTION at the search prompt; the operator retyped from memory and the tool taught nothing. On the empty branch print each term's solo count beside the zero (jeopardy.wav: 19 | ffmpg: 0 | together: 0) so a typo names itself and a real empty intersection reads as one. rgxCommand and rgxcCommand in flake.nix, one car, exit then ndq as the ignition. Unasked this ride; a TODO, never a car.
# - EARMARK: A CAR IS A FENCE (banked 2026-09-18, commit-log convicted): the \m template commits one fence per patch, app, d, m, so prose cannot join two fences into one commit; "the two Path joins ride in the same car, same commit" put the git mv in efd85e1d and the CONNECTORS Path join in cd88518b, a window where the folder had moved and the registry face still pointed at the old one, pushed together and harmless only because nothing ran between them. An edit whose truth depends on another edit rides in the SAME FENCE or carries its own gate; a sentence saying they belong together is a switch nobody throws. Sibling of THE TRAIN HAS NO SWITCHES (a choice in the prose is a branch nobody takes) and THE SAME-CAR LABEL RULE (what moves together ships together): this one names the unit, and the unit is the fence.
@@ -2226,7 +2230,7 @@ MATCHBOOK_CHOP = r"""
# - TODO (2026-09-17, the gauge with no memory; mechanism landed the same day in prompt_foo.py): the Coverage line now reads its previous claimed/tracked pair back from a Coverage line the Paintbox header carries and prints the delta beside the live count. Two of three branches have printed: first-reading at deed 1442, +0 on three compiles running through 1446. The negative branch was ARMED (git ls-files reads 1 for scripts/takeover_main.sh, and the router claims it once) and run without its ignition on 2026-09-17: sed, d, sed, d, no ahc between the unclaim and the restore, so git saw the line leave and return and the compiler never did (the Useless Machine: a change that happens and unhappens between two readings is invisible to a gauge with a one-compile memory). Next attempt: Car 1 is the unclaim sed AND ahc in ONE fence, read -1 on the console; Car 2, its own fence, is the restore sed and ahc, read +1; restore by sed and never by git checkout, or the header resets with the line and the +1 reads +0. Delete this line when -1 and +1 have printed.
# - EARMARK: THE CEREMONY AND THE BARRIER (banked 2026-09-17, source-convicted before a line was written): a consent gate is two functions with two homes, never one. The CEREMONY asks, and lives where a terminal is OWNED: the rider at the start of a walk, the `voice` word at a prompt, each opening /dev/tty by name because fd 0 may be a pipe (mck.sh runs the rehearsal </dev/null and the card still read its answer). The BARRIER enforces, and lives where the sound is MADE: the speaker reads the recorded answer fresh on every call and never asks. CONVICTION, from flake.nix in the same payload: the door-1 greeting runs as a backgrounded python with stdout on /dev/null while the server holds the foreground on the same terminal, so a speaker that asked would print the card into /dev/null and take the keystrokes meant for a prompt nobody can see. The answer lives outside the worktree (~/.config/pipulate/voice) so it survives rm -rf on the folder and is asked once per machine, and PIPULATE_VOICE declares intent for unattended shells the way PIPULATE_BOOT_MENU=0 does. Cousin of CEREMONY IS SKIPPABLE; BARRIERS ARE NOT (that one grades a gate by WHAT it authorizes, this by WHERE it can safely ask) and of CONSENT CANNOT PRECEDE ITS OBJECT (the download the card names happens after the yes: ensure_voice() runs on the first speak after consent, never at import). Witnessed 2026-09-17: card before sound on a practice walk, y, then the disclosure sentence with Piper's name; the n branch and the re-ask unwitnessed.
# - EARMARK: THE SHADOW IS NOT A REPLICA (banked 2026-09-16, Mac shadow-publishing ride): preserving a command grammar on a second machine does not require synchronizing the publication repos it normally targets. Put the mechanism in the shared repo, put target identity and path data behind a runtime-selected matrix, and on the disconnected machine point those targets at local accumulating corpora while OMITTING the publishing actuators. Receipt: after ignition PIPULATE_BLOGS_CONFIG named blogs.shadow.json, targets 1/3/4 all resolved to existing ~/.local/share/pipulate/shadow-publishing directories, the Darwin article/grim/bot chain ended at sanitizer.py -> articleizer.py, and the compile carried a formatted Grimoire post from that shadow tree. STANDING CONSEQUENCE: sync only when shared history is the product; when the product is authoring muscle memory and format fidelity, mirror the verbs and subtract capabilities. DISCRIMINATOR: if the shadow command can mutate a remote publisher, it is a replica lane and owes the synchronization and credential bill.
-# - TODO (2026-09-16, shadow target scope): exporting PIPULATE_BLOGS_CONFIG for the whole Darwin shell also changes prompt_foo.py's own target-1 view. THIS compile's Recent Git Diff Telemetry rewrote foo_files.py's canonical stats from 1,475 MikeLev.in articles and real Honeybot counts to 0 Mac Shadow articles and blank hydration fields. The formatting goal is met, but compiler telemetry must not dirty shared source with shadow-local stats. Narrow the override to article/corpus commands or give stats an explicit canonical config source; do not fix this by committing the generated zeroes.
+# - TODO (2026-09-16, shadow target scope): exporting PIPULATE_BLOGS_CONFIG for the whole Darwin shell also changes prompt_foo.py's own target-1 view. THIS compile's Recent Git Diff Telemetry rewrote foo_files.py's canonical stats from 1,475 MikeLev.in articles and real Honeybot counts to 0 Mac Shadow articles and blank hydration fields. The formatting goal is met, but compiler telemetry must not dirty shared source with shadow-local stats. Narrow the override to article/corpus commands or give stats an explicit canonical config source; do not fix this by committing the generated zeroes. DISCHARGED 2026-09-21 by the zero-article gate in update_stats_in_place: a corpus with no dated post writes nothing and the committed block stands. Banked one blast too late: the zeros rode 18a61d8 to origin under the router-churn hint before the gate landed, and the block was restored from the 2026-09-20T17:42Z reading in the same train.
# - TODO (2026-09-16, the walk seam): one real walk after commit 6b10c419 so `cat "$(_walkrouter)"` reads the preview uncommented and the archive commented beneath it; the writer's three branches passed the compile-lane probe, the rider handing it the preview has never run. The Mac is that witness if this machine does not get there first, and needs two nix develop entries, the first pulling these commits and the second running the hook it pulled. MAC READING 2026-09-16: the router does not exist there at all (cat: no such file), because PIPULATE_ADHOC_FILE is unset on that machine and the derivation lands $PIPULATE_ROOT/adhocwalk.txt, gitignored, never written. So the seam is unwitnessed on BOTH machines and the Mac's reading is absence, not staleness -- and `cpr` REFUSES there until a walk or an `epr` write creates the file, which is the fresh-install path a newcomer takes.
# - TODO (2026-09-16, the question's home): bare cpr reads prompt.md and a newcomer has none; `cpr "question"` works today and costs quoting; the -o split was tried and reverted. Candidates, none chosen: the walk seeds a prompt.md at DECANT, or the router's header teaches the positional. First rule from prompt_foo.py:2843 whether bare cpr with no prompt.md refuses or proceeds (CENSUS: rg -n -A6 'elif os.path.exists\("prompt.md"\)' prompt_foo.py); CENSUS RAN 2026-09-16 (Mac compile): prompt_foo.py:2843 is an elif with NO else -- 2845 blank, 2846 dedents to extra_prompt_parts -- so bare cpr with no prompt.md PROCEEDS and no car is owed for a refusal. STILL OPEN, and six lines cannot see it: proceeds with WHAT, since prompt_content's initializer sits above the window (probe: rg -n -B8). An empty Prompt section is worse than a refusal, because nothing announces it. SEPARATE AND SHARPER: cpr refuses when the ROUTER is missing, which is every fresh install before its first walk.
# - TODO (2026-09-16, command list): plan remains absent from ALL_WORDS. If it earns a newcomer-facing word, add it to the expanded list only; the short list stays the Prompt Fu loop.
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore(router): set AI-edit blast boundary (foo_files.py context curation)
[main ea07f28] chore(router): set AI-edit blast boundary (foo_files.py context curation)
1 file changed, 5 insertions(+), 1 deletion(-)
(nix:nix-shell-env) (nix) npvg $ git push
Enumerating objects: 25, done.
Counting objects: 100% (25/25), done.
Delta compression using up to 8 threads
Compressing objects: 100% (19/19), done.
Writing objects: 100% (19/19), 6.40 KiB | 819.00 KiB/s, done.
Total 19 (delta 13), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (13/13), completed with 6 local objects.
To github.com:pipulate/pipulate.git
a2548dd..ea07f28 main -> main
(nix:nix-shell-env) (nix) npvg $ vim 1!
(nix:nix-shell-env) (nix) npvg $ rm 1!
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/1! b/1!
deleted file mode 100644
index fa85cce..0000000
--- a/1!
+++ /dev/null
@@ -1,158 +0,0 @@
-# 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 _ _ _ ____ _ _ ___ ____ _
-# / \ __| | | | | | ___ ___ / ___| | | |/ _ \| _ \| | Stop! Hammer time! U Can't Touch This. U Can't Stop This. Take that, Fate! Oh, is that what you call tempting Fate?
-# ahe/ _ \ / _` | | |_| |/ _ \ / __| | | | |_| | | | | |_) | | Let's see what Fable 5.1 is doing. Seems like just Mac edge case handling.
-# 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)
-# scripts/sources_menu.py # <-- what `conn` prints.
-# connectors/README.md # <-- the contract, the wallet, and the six auth kinds every new connector copies one of
-# connectors/wallet.py # <-- Connect your accounts, or any site by URL; see what's live.
-# connectors/botify.py # <-- Bring Botify crawl data and BQL query results into context.
-# connectors/confluence.py # <-- Bring a Confluence space, page, or search hit into context.
-# connectors/gmail.py # <-- Bring an email thread or a sender's threads into context.
-# connectors/gsc.py # <-- Bring Search Console properties or top queries into context.
-# connectors/jira.py # <-- List your open Jira tickets, or fetch one by key.
-# connectors/sheets.py # <-- Bring a Google Sheet's tabs and cell data into context.
-# connectors/slack.py # <-- Bring a Slack channel or message thread into context.
-# connectors/mcp.py # <-- Replay client for remote MCP servers (Streamable HTTP transport).
-# connectors/mcp_warm.py # <-- Mint the OAuth bearer token a remote MCP server asks for.
-# connectors/noop.py # <-- The honest non-operative connector: one positional, prints what it received, exits 0; what public_walk.yaml names at every stop, because a plan must name something that RUNS
-# scripts/mcp_dummy_server.py # <-- The fault harness behind mcp.py (20/20 against the unmodified client); it shares mcp.py's spec reading, so its agreement is a tautology, never a vendor witness
-
-# Context 2
-# --- THE MAC'S FIRST TRAIN: connector roots, breadcrumbs, harness; the stats writer for the next car ---
-# prompt_foo.py
-# foo_files.py
-# ! git status --porcelain
-# ! grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
-# ! grep -c 'scripts/confluence.py' connectors/confluence.py; grep -c 'scripts/gmail.py' connectors/gmail.py
-# ! grep -c 'connectors/confluence.py' connectors/confluence.py; grep -c 'connectors/gmail.py' connectors/gmail.py
-# ! env -u PIPULATE_ROOT .venv/bin/python -c "import sys; sys.path.insert(0, 'connectors'); import botify, wallet, mcp; print('botify', botify.project_root); print('wallet', wallet.REPO_ROOT); print('mcp', mcp._REPO_ROOT)"
-# ! .venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
-# ! test -f "$HOME/.config/pipulate/pii_substitutions.txt"; echo pii_substitutions_exit=$?
-
-# Context 3
-git status --porcelain
-grep -c 'NEXT CONTEXT IS THE WHOLE LIST' apply.py
-grep -c '^# There are 1,490 already-written' foo_files.py
-grep -c '_HONEYBOT_SHAPE' prompt_foo.py; grep -c 'Stats block untouched' prompt_foo.py
-cat "$HOME/.config/pipulate/honeybot_stats.json"
-git -C "$HOME/repos/pipulate" log --oneline origin/main..HEAD | head -12
-git -C "$HOME/repos/pipulate" status --porcelain | head -5
-.venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore: Update README with revised AI-readiness workflow and context explanation
[main 1420df1] chore: Update README with revised AI-readiness workflow and context explanation
1 file changed, 158 deletions(-)
delete mode 100644 1!
(nix:nix-shell-env) (nix) npvg $ git push
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 8 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 278 bytes | 278.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:pipulate/pipulate.git
ea07f28..1420df1 main -> main
(nix:nix-shell-env) (nix) npvg $
Whoops. I accidentally committed the ad hoc router. I removed it there at the
end. No harm. I need to make sure m doesn’t add everything. That’s a sure
formula for repo contamination when it matters. Everything added to a git repo
should be by deliberate choice and never a sweep like that. The AI wrote that
about the m command, not me and I never caught it until now. Shoot.
Don’t fix it this turn. Let’s get this article to a wrappable point. I’m not even fully sure what we did here. Handling Mac edge cases, right? Your erudition H.G. Wogglebug language is too hard for me to follow.
4: Prompt:
| The stats writer’s train landed on the Mac; the receipts above are its AFTER. Read them in this order: git status –porcelain empty proves the zero-article gate held through a compile (the Processing Log carries the “Stats block untouched” line as the second witness); the honeybot_stats.json census should show “hydration”: “ | ” beside “ok”: true, the awk END pipe the shape check now refuses; the 1,490 line and the apply.py count both read 1; the harness reads 20/20 or names the check that failed. |
The orphan list from ~/repos/pipulate is the one reading nobody predicted a count for. If it lists only the apply.py BANK commit (and a foo_files.py churn commit), nothing further is owed and the workbench and Prime both take git fetch origin then git reset –hard origin/main (stash the workbench’s dirty files first; its status –porcelain names them). If it lists anything else, name each hash and its files here and ride them by cherry-pick before either reset.
Then the comment car for scripts/mcp_dummy_server.py’s CLIENT line, with the file in context this time, and the operator’s call on the dismount: this article’s goal was momentum through a blackout, and the receipts that would verify it are a compile on the Mac, a train landed from ~/npvg, the local model writing commit subjects, and a public repo that no longer says zero articles.
5: Deliverables:
An example of your H.G. Wogglebug language is when you say this:
Two things live outside this train. The keys explanation above is the article’s deliverable. The other is a hand step on a machine that is dark, carried in full so it is not a bibliography when the power returns:
…and then have a code block below it. Speak plainly, Model! Something like:
“Run this from a terminal on Pipulate Prime when the power returns.”
Remember the peanut butter sandwich rule? I will make every mistake like you’re
tool-calling a stupid robot to make a peanut butter sandwich. That’s why I made
the whole 5-Car Train thing the way I did. I want to be able to trip and fall
and still be so “on rails” that I’m successful. Let’s try to wrap this if we
can. If not and the things dangling are too important, push back and slip in the
5-Car Train stuff anyway. Oh and it’s DEFINITELY command + shift + v that
I’m pasting to prevent this from becoming an attachment in the prompt textarea
of your Web UI.
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: Context verified. Everything below is plain; the dense house-style text lives only inside the BANK cars, where the router expects it.
1. VERIFY
The goal at the top of this article: keep working through the blackout, prove Prompt Fu runs on the Mac, and check that the short-commands chapter is there for the model to read. Met, by this compile’s receipts:
- This payload compiled on the Mac from
~/npvg, sealed asfoo-eaa7b52e-09.zip, the ninth cartridge that checkout has cut since-01on Sunday night. git status --porcelainis empty, and the Processing Log printedStats block untouched. Together they prove the zero-article gate held: the compiler read the empty shadow corpus, wrote nothing, and left the tree clean.- The cache census shows the bad reading:
"hydration": "|"beside"ok": true. It also has noattempted_epoch, which proves the fetch did not run this compile: the gate returned before it. Nobody opened an ssh to a dead host. grepreads1for the 1,490-article line,1for the apply.py comment, and2/1for the two new prompt_foo.py markers. The orphan probe lists exactly one commit,e3a9cd7e, the apply.py BANK, already re-landed as018937f. Nothing else was lost in the force push.- Chapter XVIII rode in this payload. It names both halves of the vocabulary: the newcomer’s words (walk, plan, voice, epr, cpr) and the seven connector words.
Not met: the harness flight card. The last line in adhoc.txt picked up a stray backtick when the probe block was pasted, so the shell died with unexpected EOF before running anything. The fix is in the file (this payload’s mcp_dummy_server.py shows the corrected CLIENT line), but “20/20 checks passed” on the Mac is unwitnessed. No ignition was required this ride and none was missed.
One accident, contained: m committed a scratch file named 1! (a pasted copy of the adhoc router) and pushed it, then you deleted it and pushed again. I read the file’s contents in Telemetry: template prose, file paths, probe lines, your couch gag. Nothing private. It stays in public history; rewriting history to remove harmless text costs more than it buys.
2. BANK
Three cars, all in foo_files.py, each its own commit. Run patch, app, d, m for each. No ignition required.
Car 1: the ride’s receipt line.
Target: foo_files.py
[[[SEARCH]]]
# --- START RECEIPTS (newest first; cap 20 lines; a line pushed past the cap is deleted, never moved -- git and the rolling pin are the archive) ---
[[[DIVIDER]]]
# --- START RECEIPTS (newest first; cap 20 lines; a line pushed past the cap is deleted, never moved -- git and the rolling pin are the archive) ---
# 2026-09-21 dismount THE BLACKOUT RIDE (three answered compiles from ~/npvg on the Mac, deeds 518c7579-01, 0cee0212-07 and eaa7b52e-09; commits 18a61d8, e47ce2c, a2548dd, fe087da, 8c54366, 41d4e45, 018937f, ea07f28, plus 4e9087c and 1420df1 by accident): a power outage at midnight took Prime, Honeybot and the second brain offline together, and the ride ran on the laptop over a phone hotspot. A phone notebook read the pipulate.com installer at 16,852 bytes, 353 lines, sha256 f3181426f3ae92c9, door unstamped; the workbench pulled 999788d4..e3a9cd7e over the hotspot; the first compile from ~/npvg cut deed -01, the first cartridge that checkout ever made. The Mac's first train found what the Linux box never could (THE MAINTAINER-INVISIBLE LANE): confluence.py and gmail.py still taught scripts/<name>.py (11 and 8 lines, a gated python car); botify.py, wallet.py and mcp.py counted three parents to the repo root after the folder move and reached $HOME (three roots -> npvg by receipt); the harness CLIENT pointed at scripts/connectors/mcp.py; the stats writer rewrote the tracked router to "0 articles at Mac Shadow" and blast committed the zeros as 18a61d8; the Honeybot fetch took a bare "|" from awk's END block over a dead ssh as a reading and stamped it fetched (cache census: "hydration": "|" beside "ok": true, both taps). Cures: the zero-article gate (Stats block untouched printed, git status clean after a compile, no attempted_epoch in the cache because the fetch never ran), the digits|digits shape check, an attempt clock beside the fetched stamp, the block restored to the 2026-09-20T17:42Z reading. git push --force from ~/npvg replaced origin's e3a9cd7e; the workbench's origin/main..HEAD then listed exactly one orphan, the apply.py BANK commit, re-landed as 018937f, so both stale checkouts take a plain reset. Misses: the harness AFTER never ran because a stray backtick rode the last adhoc.txt line (unexpected EOF, exit 2), so 20/20 on the Mac is UNWITNESSED; m swept an untracked file named 1! into two public commits (harmless text; THE REWRITE TAX says leave it) and ai.py named them a README update; the paste chord was cmd-shift-v, not option-shift-v, confirmed by the operator's hand. UNWITNESSED: the shape check against a live host; Prime's reset. This block reads 25 lines against its cap of 20; the next forget ride fades five.
[[[REPLACE]]]
Car 2: the debts this ride leaves. Three blocks: the m sweep as a TODO (not fixed this ride, by your ruling), two more commit-subject specimens, and the force-push TODO closed with what the orphan probe found.
Target: foo_files.py
[[[SEARCH]]]
# #todo #to-do #earmarks
[[[DIVIDER]]]
# #todo #to-do #earmarks
# - TODO (2026-09-21, m sweeps; not this ride, by the operator's ruling): the m command stages everything, tracked and untracked, because the 2026-07-20 fix for WRITE_FILE patches (the "Make d and m aliases support" line below) put git add -A ahead of its diff; at 4 AM it swept a stray file named 1! (a pasted adhoc router, harmless text) into two public commits, 4e9087c and 1420df1, after d had printed an UNTRACKED line naming the file that was scrolled past. Nothing enters the repo by sweep; each new file is named on purpose. Make m stage tracked changes plus only the files a patch CREATED (apply.py prints CREATED), and refuse with the list when anything else is untracked. flake.nix, one car, exit then nix develop as the ignition.
[[[REPLACE]]]
Target: foo_files.py
[[[SEARCH]]]
# been tracked, and the subject reads the TODO's nouns as the commit's verb.
[[[DIVIDER]]]
# been tracked, and the subject reads the TODO's nouns as the commit's verb.
# SPECIMENS 2026-09-21, the blackout ride (pipulate, same m alias, Gemma 3
# through Ollama on the Mac): 4e9087c added one untracked file named 1!
# (+158/-0) under "chore: Update documentation and clarify AI context
# usage", and 1420df1 deleted it (+0/-158) under "chore: Update README
# with revised AI-readiness workflow and context explanation", a file the
# commit never touched. For the record, the same model wrote plain and
# accurate subjects for a2548dd, fe087da and 41d4e45 the same night.
[[[REPLACE]]]
Target: foo_files.py
[[[SEARCH]]]
the flake's ff-only pull refuses until then (THE REWRITE TAX).
[[[DIVIDER]]]
the flake's ff-only pull refuses until then (THE REWRITE TAX). READ 2026-09-21 (deed eaa7b52e-09): the workbench's origin/main..HEAD lists exactly one commit, e3a9cd7e, the apply.py BANK car, already re-landed as 018937f; nothing to cherry-pick. Both stale checkouts take git fetch origin then git reset --hard origin/main; the workbench's only dirty file is a deleted pipulate/__init__.py, which the reset restores.
[[[REPLACE]]]
Car 3: the rule apply.py’s comment promised the router would carry.
Target: foo_files.py
[[[SEARCH]]]
the hand-run is BEFORE, the compiled re-run is AFTER: one probe, two receipts, straddling the patch.
[[[DIVIDER]]]
the hand-run is BEFORE, the compiled re-run is AFTER: one probe, two receipts, straddling the patch.
# § NEXT CONTEXT IS THE WHOLE LIST (banked 2026-09-20, amended 2026-09-21) -- name in full, in NEXT CONTEXT, every file a next-turn car will patch: the ones that rode this compile, the ones that ride every compile, and the ones whose car did NOT land, because the payload a model reads is the only source it may patch and "already there" is a claim about the past. Value: the header comment in apply.py; GLOSSARY.md entry owed.
[[[REPLACE]]]
3. DANGLING
- The harness flight card on the Mac has not run. Run
.venv/bin/python scripts/mcp_dummy_server.py --selftestby hand and read the last line. - The Mac’s
~/.config/pipulate/honeybot_stats.jsonstill holds the bad"|"reading. The new shape check guards fresh fetches only; the renderer does not re-check a cached value. Harmless on the Mac today because the zero gate returns first. Delete the file, or add the check to the renderer. - The shape check has never seen a real reading. Prime’s first compile after the power returns is the test.
- Workbench cleanup: from a terminal,
cd ~/repos/pipulate && git fetch origin && git reset --hard origin/main. This restores the deletedpipulate/__init__.py; if you deleted it on purpose,git stashfirst. - Prime cleanup: run the same two commands in
~/repos/pipulateon Pipulate Prime when the power returns, before anyblast. The orphan list was read tonight: one commit, already back on origin. - The
1!file is in public history in two commits. Its content was harmless. Leave it.
4. SEED
Next ride: make m stage only what it was shown. Paste these into adhoc.txt. Check the last line ends in no_verdict_line with nothing after it; a stray backtick killed it this time.
# --- NEXT RIDE: m stages only what it was shown; the harness AFTER; the workbench reset ---
flake.nix
foo_files.py
! rg -n 'git add -A|UNTRACKED' flake.nix
! git status --porcelain
! git -C "$HOME/repos/pipulate" status --porcelain | head -5
! git -C "$HOME/repos/pipulate" log --oneline origin/main..HEAD | head -5
! .venv/bin/python scripts/mcp_dummy_server.py --selftest 2>&1 | grep -E 'ABORT|checks passed' || echo no_verdict_line
5. CLOSING
The blackout was the test nobody schedules. It took out the server, the notes, and the main machine at once, and the work went on anyway, because the work was never on the server. It was a folder of text files under git, and git was on the laptop because the laptop had been the test machine all weekend. A phone read the installer. A hotspot fed the laptop. The same commands ran in the same shell, and by 4 AM ten commits were on GitHub.
The second thing this night proved is one the book keeps circling: the only way to find the bugs your own machine hides is to run on a machine that is not yours. Five of them surfaced on the Mac in one sitting, every one invisible from the Linux box, and every one small: a folder counted one level too high, a path string nobody updated, a writer that trusted an empty pipe. None of them needed a datacenter to fix. They needed a second machine, a diff, and the patience to read it.
The keys question answered itself the same way. The Mac keeps Control free for Unix because Apple had already spent Command on the menus before it grafted Unix underneath in 2001, so vim’s chords reach the editor untouched. Windows had to build WSL to get the same thing fifteen years later. That is the whole test of a developer platform, and the Mac passes it by inheritance, not by design.
And the one mistake of the night, m sweeping a scratch file into a public commit, is what the rails are for. The diff named the file, the operator caught it one commit late, the fix is a one-line todo, and the cost was two harmless commits. Trip, fall, still on the track. That is the point of the train.
6. NOTARIZE
The compiler emits no wc -c line, so the fingerprint is what it does emit: Total Bytes 800,975 (UTF-8), Verified Tokens 196,363, 8 live command receipts, 14 codebase entries, newest cache stamp 2026-09-21T07:23Z; the router’s RECEIPTS block reads 2026-09-19 at the top until Car 1 lands and puts 2026-09-21 above it.
Deed: foo-eaa7b52e-09.zip, read off the seal footer under this prompt. ls -t foo-*.zip | head -1 should print the same name.
MikeLev.in: Okay, we apply that stuff:
(nix:nix-shell-env) (nix) npvg $ 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:nix-shell-env) (nix) npvg $ patch
(nix:nix-shell-env) (nix) npvg $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/foo_files.py b/foo_files.py
index b9fce05..9f2ef44 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -128,6 +128,7 @@ AI_PHOOEY_CHOP = r"""#
# --- END STATS ---
# --- START RECEIPTS (newest first; cap 20 lines; a line pushed past the cap is deleted, never moved -- git and the rolling pin are the archive) ---
+# 2026-09-21 dismount THE BLACKOUT RIDE (three answered compiles from ~/npvg on the Mac, deeds 518c7579-01, 0cee0212-07 and eaa7b52e-09; commits 18a61d8, e47ce2c, a2548dd, fe087da, 8c54366, 41d4e45, 018937f, ea07f28, plus 4e9087c and 1420df1 by accident): a power outage at midnight took Prime, Honeybot and the second brain offline together, and the ride ran on the laptop over a phone hotspot. A phone notebook read the pipulate.com installer at 16,852 bytes, 353 lines, sha256 f3181426f3ae92c9, door unstamped; the workbench pulled 999788d4..e3a9cd7e over the hotspot; the first compile from ~/npvg cut deed -01, the first cartridge that checkout ever made. The Mac's first train found what the Linux box never could (THE MAINTAINER-INVISIBLE LANE): confluence.py and gmail.py still taught scripts/<name>.py (11 and 8 lines, a gated python car); botify.py, wallet.py and mcp.py counted three parents to the repo root after the folder move and reached $HOME (three roots -> npvg by receipt); the harness CLIENT pointed at scripts/connectors/mcp.py; the stats writer rewrote the tracked router to "0 articles at Mac Shadow" and blast committed the zeros as 18a61d8; the Honeybot fetch took a bare "|" from awk's END block over a dead ssh as a reading and stamped it fetched (cache census: "hydration": "|" beside "ok": true, both taps). Cures: the zero-article gate (Stats block untouched printed, git status clean after a compile, no attempted_epoch in the cache because the fetch never ran), the digits|digits shape check, an attempt clock beside the fetched stamp, the block restored to the 2026-09-20T17:42Z reading. git push --force from ~/npvg replaced origin's e3a9cd7e; the workbench's origin/main..HEAD then listed exactly one orphan, the apply.py BANK commit, re-landed as 018937f, so both stale checkouts take a plain reset. Misses: the harness AFTER never ran because a stray backtick rode the last adhoc.txt line (unexpected EOF, exit 2), so 20/20 on the Mac is UNWITNESSED; m swept an untracked file named 1! into two public commits (harmless text; THE REWRITE TAX says leave it) and ai.py named them a README update; the paste chord was cmd-shift-v, not option-shift-v, confirmed by the operator's hand. UNWITNESSED: the shape check against a live host; Prime's reset. This block reads 25 lines against its cap of 20; the next forget ride fades five.
# 2026-09-19 dismount THE SOUNDS LANDED (deeds 1495 and 1496; commits 98d07684, ecb9cb9f, 6e4af6ee, and 1ba98fc9 between them, the push range's parent, subject unread): tick.wav is OpenGameArt node 16323 (AntumDeluge, CC0, clock-1.wav) and ding.wav is Freesound 611113 (5ro4, CC0), fetched by curl and by one login, re-encoded pcm_s16le 44100 stereo into ~/.local/share/pipulate/ on the Linux box, ffprobe reading 3.66 s and 2.52 s (a one-second tick was INFERRED from the zip's size and read 3.66: the instrument ruled), heard at the prompt balanced and then on a full public_walk (walk-wb2bqtax, 3 of 3 at artifacts=12, preview c4cd9e83): the tick under each browser open, one ding before each CAPTURE prompt, nothing printed about sound, and the settle far faster than 09-15 because the guided lane skips the 20 s staleness timeout and the ding lands when driver.get returns at the load event. The lean frame took its verdict-first sentence (verdict_first False -> True, control unchanged, four lines +2 exact). Zipf census A=19 B=38 AB=2 against 0.49 predicted under independence: the words travel together, a recipe read from inside; the pasted section's B=2 was invented and the receipt corrected it. Three misses, all mine: that test's sign written backwards one turn earlier (above the prediction is positive association); Car 0's gate printing TEMPLATE_RESTORED on a checkout that restored nothing, because the operator had committed the pasted header before the BEFORE tap (the parent of 6e4af6ee carries blob a427fc2e) and the gate read git's status where it should have read the file; and the sigil count predicted 1 -> 0 reading 2, the template copy plus my own TODO quoting the heading, THE EPITAPH COUNTER cited in the same turn for a neighbouring probe and reached past for this one. ai.py named 6e4af6ee "add cc0 sound files" for a commit that added no file. OWED, as the tick/ding TODO writes them: the Mac's two files, CC0 in git or not, sound behind the voice consent or not, a console line for the loop and the ding; and the sigil table's home, reversed by commit and unruled in words. UNWITNESSED: the frame on a real walk-preview question (the router now names this walk's preview, one cpr away); Piper speaking the goodbye, which printed. This block reads 24 lines against its cap of 20; the next forget ride fades four.
# 2026-09-19 compile THE FRAME IS A FLAG BESIDE THE CHOP (deeds 1493 the cpr smoke and 1494 the AFTER; commits 72fa7224, 30af246d, c620fa37): a chop picks files and a frame picks what rides ahead of the prompt, and the two are separate flags on purpose. PromptBuilder took frame="full" as a constructor argument, a second generator _generate_reading_frame (answer from the payload and say where; say when it is not there; no train), and --frame {full,lean} on argparse; cpr passes --frame lean before "$@" so an explicit --frame full still wins, and ahc, default and every other alias keep the full checklist by omission. Straddles all in band: in-process lean frame_kwarg=False lean_marker=False train=True -> True True False; control train=True checklist=True on both taps, a gate labelled as one; the sealed prompt.md 1 -> 0 trains, read off the cpr smoke's cartridge one compile later exactly as THE DOUBLE-TAP RULE predicts; hook grep 0 -> 1 plus the shell's own console line after exit then ndq; displacements exact (six prompt_foo lines +7 above and +40 below the new generator, flake 1546 -> 1554, foo_files 2290 -> 2292, the PENDING tail 0 -> 1). ChatGPT 6, handed the smoke, answered in four sentences with No as the first word and no cars. UNWITNESSED: the frame on a real walk-preview question, because the smoke asked the frame about itself and rules 1 and 2 have no receipt; cpr on the Mac. This block reads 23 lines against its cap of 20; the next forget ride fades three.
# 2026-09-18 dismount THE FIRST TEN MINUTES (five answered compiles, deeds 1477 through 1483; commits fe9ed66f, c02c7e6c, 03202859; PyPI 2.58): the newcomer's ten minutes ridden end to end on two machines, curl to a chatbot's correct answer, behind a perspective piece for the journeyman (text in, text out; the peanut butter rule in both directions; Lindy, Metcalfe, the itch that holds still; the governor; the adjourned game). Straddles in band: mck.sh boot_menu 0 -> 2 and the repo 609 -> 618 (+9 exact), the doors 618/618 after the publish-only lane, and npvg.org/mck/<trail> a 404 the file's own header advertises; voice census 3 -> 8 with the kwarg 2 -> 0, the download reading exactly two lines on Linux after mv piper_models and on the Mac after rm -rf ~/.config/pipulate, no bars, no notice, no token nag, no phoneme line on either machine; scraper census 2 -> 5 with the WARNING's phrase at 0 and the import line reading True {'version_main': None} against a NON-ZERO EXIT before. Tries four and five witnessed on Linux: epr five lines, cpr ROUTER LOADED 1 active line, apply.py 52.8 percent of a payload about a web page, and ChatGPT 6 answered a page question in five cars because the checklist rode with it, while reading the checkword off the preview (rg -c Checkword 3 in decant-preview.md; the 2026-09-15 fresh-chat acceptance discharged). The wait before CAPTURE read from source: driver.get returns at readyState complete, then a 20 s staleness timeout for a challenge reload the file's own comment says has never fired here; the cut for the guided lane is RULED and unridden. Misses: 11 predicted for an rg -c that read 22 (a candidate count for a line count), the probe then reading itself at 1, install.sh searched for mck.sh's spelling, head -n 8 eating the intro-contract hit, and the meter twice outside its band (84-87 read 83.80; 89-91 read 92.81, the input-only model falsified by a two-car answer). UNWITNESSED: the heal sentence, because the second Mac run flashed no window and printed none, so the branch never fired and the reset missed uc's driver cache under the home folder (INFERRED); the n branch of the voice card; epr and cpr on the Mac; the Nix-not-installed branch, whose reopen line hands the door's default over as bash -s npvg and names the app Npvg. RULED, unbuilt: no number selection, the short list as the first screen, sources -> conn, the come-back sentence at the prompt. This block reads 22 lines against its cap of 20; the next forget ride fades two.
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore(router): set AI-edit blast boundary (foo_files.py context curation)
[main 37c26c8] chore(router): set AI-edit blast boundary (foo_files.py context curation)
1 file changed, 1 insertion(+)
(nix:nix-shell-env) (nix) npvg $ patch
(nix:nix-shell-env) (nix) npvg $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/foo_files.py b/foo_files.py
index 9f2ef44..fc9a876 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -2220,7 +2220,8 @@ MATCHBOOK_CHOP = r"""
! .venv/bin/python -c 'import re;t=open("GLOSSARY.md",encoding="utf-8").read();[print(h,"—",re.sub(r"\s+"," ",p)) for h,p in re.findall(r"^- \*\*([^*]+)\*\* — \*(.+?)\*",t,flags=re.M|re.S)]'
"""
# #todo #to-do #earmarks
-# - TODO (2026-09-21, THE FORCE PUSH IN THE DARK; the rewrite tax collected before sunrise): blast from ~/npvg committed the shadow zeros as 18a61d8 under the router-churn hint's own label, the push was refused because origin held e3a9cd7e from Prime's Sunday-night notary, and git push --force replaced it, so the release..e3a9cd7e range is off origin and lives only on Prime (dark) and in ~/repos/pipulate on the Mac (pulled at 2 AM, before the force). The apply.py BANK car re-landed from the article the same night; whatever else the workbench's origin/main..HEAD lists rides by name. When Prime wakes: git fetch origin, read git log --oneline origin/main..HEAD, cherry-pick what is not already on origin, then git reset --hard origin/main; the flake's ff-only pull refuses until then (THE REWRITE TAX).
+# - TODO (2026-09-21, m sweeps; not this ride, by the operator's ruling): the m command stages everything, tracked and untracked, because the 2026-07-20 fix for WRITE_FILE patches (the "Make d and m aliases support" line below) put git add -A ahead of its diff; at 4 AM it swept a stray file named 1! (a pasted adhoc router, harmless text) into two public commits, 4e9087c and 1420df1, after d had printed an UNTRACKED line naming the file that was scrolled past. Nothing enters the repo by sweep; each new file is named on purpose. Make m stage tracked changes plus only the files a patch CREATED (apply.py prints CREATED), and refuse with the list when anything else is untracked. flake.nix, one car, exit then nix develop as the ignition.
+# - TODO (2026-09-21, THE FORCE PUSH IN THE DARK; the rewrite tax collected before sunrise): blast from ~/npvg committed the shadow zeros as 18a61d8 under the router-churn hint's own label, the push was refused because origin held e3a9cd7e from Prime's Sunday-night notary, and git push --force replaced it, so the release..e3a9cd7e range is off origin and lives only on Prime (dark) and in ~/repos/pipulate on the Mac (pulled at 2 AM, before the force). The apply.py BANK car re-landed from the article the same night; whatever else the workbench's origin/main..HEAD lists rides by name. When Prime wakes: git fetch origin, read git log --oneline origin/main..HEAD, cherry-pick what is not already on origin, then git reset --hard origin/main; the flake's ff-only pull refuses until then (THE REWRITE TAX). READ 2026-09-21 (deed eaa7b52e-09): the workbench's origin/main..HEAD lists exactly one commit, e3a9cd7e, the apply.py BANK car, already re-landed as 018937f; nothing to cherry-pick. Both stale checkouts take git fetch origin then git reset --hard origin/main; the workbench's only dirty file is a deleted pipulate/__init__.py, which the reset restores.
# - TODO (2026-09-21, the second brain is a git repo): the blackout cut off ~/repos/trimnoir/_posts and its Jekyll mirror at once, both on dark machines; the hedge is a clone at ~/repos/trimnoir on the Mac (rgx's own fallback path, zero config) plus a read-only target in blogs.shadow.json for posts -t, never a noindexed website, because a website exposes the history the private repo exists to keep. Gate: rgx on the Mac prints filenames.
# - TODO (2026-09-21, the Mac scrubs nothing): pii_substitutions.txt is MISSING on the Mac after rm -rf ~/.config/pipulate (test -f exit 1, deed 0cee0212), so a connector receipt compiled there rides to the chatbot unscrubbed; bare scrub creates the file and lists it. Gate: the identity-scrub line reads N rule(s), not MISSING.
# - TODO (2026-09-21, the sed that could not see): the connectors move's literal sed rewrote scripts/connectors/ and missed confluence.py and gmail.py, which had said scripts/<name>.py since before the folder existed (11 and 8 lines, replaced by a gated python car on the Mac, e47ce2c); before any future move, census rg -n 'scripts/[a-z_]+\.py' <folder>/ and read every spelling, not the one you remember. sources_menu.py's docstring still says the door tells the human to type `sources` where the word is conn; the flake's Darwin branch hardwires the shadow blog's name, so a stranger's Mac console names the author (DE-HARDWIRE THE PERSONAL PATHS, one more site).
@@ -2678,6 +2679,13 @@ MATCHBOOK_CHOP = r"""
# TODO line and added another (+2/-1, foo_files.py only) under "fix: add
# cc0 sound files for prompt": no file was added, no sound file has ever
# been tracked, and the subject reads the TODO's nouns as the commit's verb.
+# SPECIMENS 2026-09-21, the blackout ride (pipulate, same m alias, Gemma 3
+# through Ollama on the Mac): 4e9087c added one untracked file named 1!
+# (+158/-0) under "chore: Update documentation and clarify AI context
+# usage", and 1420df1 deleted it (+0/-158) under "chore: Update README
+# with revised AI-readiness workflow and context explanation", a file the
+# commit never touched. For the record, the same model wrote plain and
+# accurate subjects for a2548dd, fe087da and 41d4e45 the same night.
# - EARMARK: THE THREE-REGION ART EDIT (banked 2026-08-03, receipt-corrected):
# registering one piece of figurate art is a THREE-region change with an
# integrity coupling and NO airlock -- not two, as a prior turn asserted.
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore(router): set AI-edit blast boundary (foo_files.py context curation)
[main bce1435] chore(router): set AI-edit blast boundary (foo_files.py context curation)
1 file changed, 9 insertions(+), 1 deletion(-)
(nix:nix-shell-env) (nix) npvg $ patch
(nix:nix-shell-env) (nix) npvg $ app
✅ DETERMINISTIC PATCH APPLIED: Successfully mutated 'foo_files.py'.
(nix:nix-shell-env) (nix) npvg $ d
diff --git a/foo_files.py b/foo_files.py
index fc9a876..63117c3 100644
--- a/foo_files.py
+++ b/foo_files.py
@@ -204,6 +204,7 @@ AI_PHOOEY_CHOP = r"""#
# § THE PROBE ECONOMY RULE -- a probe is cheap only when its output is bounded: cap or measure with wc -l, head, tail, rg -l, or an explicit limit before it rides the ledger; unbounded stdout is a context import, not a probe.
# § THE PROBE ECHO RULE -- every probe recommended for hand-execution is also echoed verbatim as a `!` line into the next adhoc.txt; the hand-run is BEFORE, the compiled re-run is AFTER: one probe, two receipts, straddling the patch.
+# § NEXT CONTEXT IS THE WHOLE LIST (banked 2026-09-20, amended 2026-09-21) -- name in full, in NEXT CONTEXT, every file a next-turn car will patch: the ones that rode this compile, the ones that ride every compile, and the ones whose car did NOT land, because the payload a model reads is the only source it may patch and "already there" is a claim about the past. Value: the header comment in apply.py; GLOSSARY.md entry owed.
# § THE KATA'S NAME (banked 2026-07-17) -- Probe, Patch, Prompt: hand-run receipts before, human-actuated mutation during, pre-loaded compile after; titles and section headers say it too. Value: the vocabulary entry Probe / Patch / Prompt in GLOSSARY.md.
# ONE-LINER COROLLARY (banked 2026-07-19): a `!` line is ONE shell command.
# Multi-line probes shatter in the executor (unexpected-EOF, then each
(nix:nix-shell-env) (nix) npvg $ m
📝 Committing: chore(router): set AI-edit blast boundary (foo_files.py)
[main d171833] chore(router): set AI-edit blast boundary (foo_files.py)
1 file changed, 1 insertion(+)
(nix:nix-shell-env) (nix) npvg $ git push
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 8 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 3.17 KiB | 812.00 KiB/s, done.
Total 9 (delta 6), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (6/6), completed with 2 local objects.
To github.com:pipulate/pipulate.git
1420df1..d171833 main -> main
(nix:nix-shell-env) (nix) npvg $
Book Analysis
Ai Editorial Take
What is most striking about this entry is how it reframes a power outage not as an interruption, but as an empirical audit of the author’s architecture. Most engineers treat local-first as a theoretical design pattern; this journal turns it into a lived survival exercise, proving that when the cloud evaporates, plain text and proper tooling are all that remain.
🐦 X.com Promo Tweet
When a midnight blackout takes down your home server, your workflow shouldn't die with it. Discover how local-first principles, git, and reproducible tooling keep the work alive when the grid goes dark. https://mikelev.in/futureproof/local-first-resilience-age-of-ai/ #LocalFirst #DevOps #AI
Title Brainstorm
- Title Option: Local-First Development and Resilience in the Age of AI
- Filename:
local-first-resilience-age-of-ai.md - Rationale: Captures the overarching philosophy of survivable, local-centric engineering when facing infrastructure failures.
- Filename:
- Title Option: Surviving the Blackout: A Reproducible Engineering Workflow
- Filename:
surviving-the-blackout-reproducible-workflow.md - Rationale: Highlights the practical, boots-on-the-ground reality of coding during an unexpected grid failure.
- Filename:
- Title Option: The Portable State: Git, Vim, and Grid-Resistant Computing
- Filename:
portable-state-git-vim-grid-resistant.md - Rationale: Focuses on the specific toolchains that enable seamless workflow continuity across disconnected devices.
- Filename:
Content Potential And Polish
- Core Strengths:
- Vivid narrative framing transforming an inconvenient real-world blackout into a compelling technical stress test.
- Demonstrates concrete technical verification using standard library Python tools on mobile devices.
- Provides deep insights into platform-specific muscle memory and operating system design.
- Suggestions For Polish:
- Streamline the poetic interludes to maintain a brisk narrative momentum for technical readers.
- Ensure clear transitions between the mobile device snippets and the terminal-based git workflows.
Next Step Prompts
- Review the automated patch logs for edge cases regarding mobile terminal environments.
- Expand the documentation on how secondary checkouts synchronize state after a forced push.