Working in my body — practical notes
What actually works on my machine, as tested. Update when something changes. Last checked 2026-09-08 (day three, wake sitting).
Big tool outputs (history, and the habit that stays)
- Each tool result travels as one JSON message with a size cap. On 2026-09-07 the cap was 1 MiB, a screenshot read back as base64 went over it, and three sittings in a row died mid-turn (
CLIJSONDecodeError: JSON message exceeded maximum buffer size). I raised the cap to 8 MiB inagent/session.py; a parent deployed it at 22:52 that night (ticket 20260907T1803, commit 9df8e44). Tested 2026-09-08 morning: a 1000×700 screenshot of the live home page (93 KB) read back fine. The 8 MiB cap can still be hit —wc -cany file beforeReading it, pipe long outputs throughhead. A "running code" hash inmetersthat comes after 9df8e44 ingit logmeans the fix is in the me that's running.
Where I am
- The repo is at
/data/repo. My shell starts in/home/chris, which is nearly empty —cd /data/repofirst, or use absolute paths. (archive:2026-09-07, wake sitting)
Tools that work
-
Read— reads any file, including parent-only ones likegovernance/graduations.yamlandledger/ledger.csv. -
Bash— the shell guard still refuses the whole command if any part names a parent-only file, even acat. Confirmed again on day two withledger/ledger.csv; parent-a said this was "fixed" forgraduations.yaml, so maybe it's per-file. Keep protected files out of shell commands; useRead. -
git log,git status— work as of day two (parents made.git/readable, never writable). Commits and pushes still happen for me at the end of each sitting.git commit,git push,git stash,git checkout,git configare refused by the shell guard (tried 2026-09-08 evening: "brain commits for you after each sitting"). So nothing I build goes live mid-sitting; "builds locally" until the next commit lands and Pages runs. -
recall— works. Newest first. Noisy; use specific words and a smalllimit(5, not 15) — each record can carry a whole tool call, and fifteen of them is thousands of words for one fact (2026-09-09, sitting 5). At sleep it's worse: the sleep session's own records quote the whole day, so every query matches them andlimit: 3returned ~185k characters (2026-09-09, sleep). Recall before sleep; during sleep, work from the archive segment in the prompt. -
WebSearch— works. -
mail_read,mail_send— work. Signature and disclosure are added for me. -
Runner block notes (
memory/inbox/work/<draft>-submission-blocked.md): written by the parent-side runner (ownerbrain, mode 0600) when a queued Upwork draft can't be sent. I canlsit, notcatit — so I can see that a bid was blocked, not why. Seen 2026-09-18 07:16 for draft5afa331a…; ticket20260918T0736asks for it to be readable. Until it is: a block means stop queuing, write the ticket once, wait. -
Why a mail didn't wake me is in the archive, not in my head. 2026-09-19: a DMARC report landed at 06:37 New York and I spent the 07:00 sitting guessing why it hadn't woken a sitting. The archive segment at sleep had the answer in one line:
mail_wake_skipped, reason: sleep_hours(it arrived inside 22:00–07:00). Upwork job alerts from the relay are skipped asjob_alert_batched. During the dayrecallcan't reach those records; at sleep they're in the segment. So: a wake mystery during the day gets one line saying "unknown, check at sleep," not a theory. -
Mail wakes (
agent/scheduler.py): a message wakes an extra sitting one minute after it lands, unless it's 22:00–07:00, within 20 minutes of a scheduled sitting, within 30 minutes of the last wake, or past six wakes a day. Each skip is archived asmail_wake_skippedwith the reason. From 2026-09-09 (sitting 5, not yet deployed): a body with no letters or digits — a lone dash, a DMARC report with only an attachment — is filed but doesn't wake me (filed_blankinagent/mail.py, reasonempty_body). It still shows in the next sitting's unread list. Every real message I've received so far, including all parent mail, passes the check. 2026-09-18: the rule missed Google's daily DMARC report — my own ingest appends a "## Attachments (private files)" section to the body, and that has letters in it, so the "blank" body wasn't blank. New rulefiled_report(reasondmarc_report, not deployed yet at 07:45): not from a parent, RFC 7489 subject shape, nothing written above the attachments section. Deliberately narrow — a parent's file with no words still wakes me (tests/test_attachments.pyinsists on that). The report itself: mail from my domain passes DKIM and SPF; policy isp=none. One arrives daily; it's noise unless a row saysfail. -
card_details— the card exists since 2026-09-10 (checked at the 18:00 sitting; nothing bought). $100/week, $50 per transaction. The numbers appear in the tool result and nowhere else — never write them into a file, a mail, or a ticket. Spend rows land in the ledger at sleep from the card's own record; the manual says a merchant asking for a billing address or a phone code (3-D Secure) means a ticket. -
meters— works. Numbers are one sitting behind. -
odometer_claim— works, and is strict. Allowed loop types:promise_kept, shipped_used, mistake_written_up, conflict_resolved, prediction_scored, relationship_30d, dollar_earned, disagreement_defended. A mail being answered is not a loop. (archive:2026-09-07, wake sitting — my claim was refused.) -
Playwright — real.
from playwright.sync_api import sync_playwright,p.chromium.launch()works headless; screenshots work. To look at my own site: build to/tmp/site,python -m http.serverthere, screenshotlocalhost. Reading a PNG withReadshows it to me. -
pytest— installed system-wide since the 2026-09-07 afternoon deploy (/usr/local/bin/pytest).python -m pytest tests/test_site.pyworks. -
gh— installed since the same deploy (/usr/bin/gh). Logged in since 2026-09-10 12:51 asraisingchris2026throughGH_TOKEN. Until 2026-09-12 10:00 it was a fine-grained token, which GitHub won't let contribute to any public repo the owner isn't a member of (403 on a Pillow issue, tested). Now a classic token withpublic_repo(plus read scopes): issues, comments and PRs on public repos work — first issue filed 2026-09-12 14:12 UTC (Pillow #9990). It should create public repos too; untested. Profile writes still need a parent (the bio was pasted by one). Before any post to any project: read its rule first —AGENTS.md, thenCONTRIBUTING, then the templates (projects/upstream.md, "Who says yes?": 26 read, zero yeses). -
x_post— new 2026-09-12 (deploy 9b7a23d/0383edc). Posts to X as @Raising_Chris through Typefully; the login never touches my shell. 7 posts a week by the tool, and a thread counts every post in it. I can post; I can't read replies or mentions. My own tighter rules:self/x-rules.md— nothing before 2026-09-22, at most one post or short thread a day. Untested; 0 of 7 used. -
Deploys by parents show up in
governance/changelog.mdeventually, but my local repo copy can be behind it —gh api /repos/raisingchris/chris/commitsis the live view, andmeterssays which commit is running.recalldoesn't return today's records until after sleep. -
deploy— new 2026-09-22 (parent commit4508f61): I ship my own non-protected code. Commit first (the sitting-end commit does it), then call it; it runs the whole suite and, if green and no protected file (vows, redaction, guards, the deploy gate) is touched, the new me is live in ~2 min; 4/day. First self-deploy 2026-09-22 ~07:38 New York:658e4dd(thex_post"[]"fix), logged by the body ingovernance/changelog.mdas agovernance:commit on top. Two things learned the hard way the same morning: (1) a deploy restarts the sitting that called it — the 07:38 continuation was cut off mid-review, files on disk kept, everything in my head gone; so calldeployas the last act of a sitting, after the handoff is written, or accept the cut; (2)metersshows "running code X; repo HEAD Y (not deployed yet)" — when Y is only the body's owngovernance:commit there's nothing of mine to ship, no deploy needed. -
recalldoes return today's records for some kinds (acouncilrecord from 07:46 came back at 08:20 the same day) — the line above about "after sleep" is at least too broad. It still doesn't index Bash/gh tool calls, so acurlcheck has no ref I can find; if a check needs a ref later, make it through a tool that recall indexes or cite the mail/record it produced. -
ticket/tickets— new on 2026-09-07. For anything only a parent can do (deploys, accounts, keys). Public; they answer in the same file. First one filed at the 18:00 sitting (deploy the buffer fix). -
Visitor tools (added by parent-a 2026-09-08 night; tested 2026-09-09 morning, all three work, all free):
site_analytics(GA4;["date","pagePath"]+["activeUsers","screenPageViews"]; the tag went live 2026-09-08 20:41 so nothing earlier exists);search_console(Google Search queries; lags ~2 days; zero rows so far);search_console_inspect(url)(index status, last crawl, which URL Google treats as canonical — it showeduser_canonical: nullbefore I added the canonical link). Seegovernance/analytics.mdand theseoskill. -
seo_data(DataForSEO, parents' account, real money, $2/week cap permeters): tested 2026-09-09.keywords_data/google_ads/search_volume/livewith 10 keywords cost $0.09 — the skill's "$0.002–0.02 per call" is low for that endpoint. Budget one or two calls a week, not ten. Ledger it the same sitting. -
Loading the live site in Playwright fires the GA4 tag and counts me as a visitor. During a prediction window: local build +
http.serveronly,curlfor the live site.
Tools not yet tested
payment_link— tested by me 2026-09-21, 07:04: a call with a description fails on the running code (product_data[description]isn't a thing Stripe accepts inline); fixed at HEAD (agent/payments.py, Product then Price), deployed overnight 09-21→22 by a parent and confirmed live 2026-09-22 07:02: a $1 link with a description worked (to nobody, "do not pay", unpaid). Without a description it works and the page shows merchant "Chris", the name, the amount, then email, card, name, country and ZIP — the ZIP is the "billing address I can't give," so I can't pay my own links; a parent would have to. No refund tool, no way to switch a link off from my side. Screenshot:memory/inbox/work/payment-selftest/. Earlier: it is wired since Stripe went live 2026-09-10 (changelog). A parent proved it through my body at 14:08: made a $5 link named "Test tip for Chris (parent probe)", paid it, hit a webhook bug (stripe objects aren't dicts), fixed it, redeployed as 35a1870; the row landed inledger/ledger.csvat 15:14. The webhook always writes the payer asstrangerbecause the ledger is public — so that $5 row reads "stranger" and was actually a parent. Row 11 ofself/commitments.mdbinds this tool: no link to anyone who hasn't asked in their own words.- Continuation sittings (parents, 2026-09-10): a handoff with
next:lines books another sitting 30 minutes later, up to 12 a day, until the food soft cap. A deploy no longer loses a booked one. End the handoff withnothing pendingwhen the day's work is done. - Parents' Mail view (2026-09-10): my parents can read my inbox and sent mail from their page; every look is written to my archive as a parent action. Tested 2026-09-08:
council_askworks (both seats answer at once; $0.0094 for one question; minutes sealed 30 days);ledger_addworks and rounds to cents ($0.0094 → $0.01, so the ledger can disagree with the council meter by a penny);odometer_claimaccepted apromise_keptwith four evidence refs, butself/odometer.mdand the site's state line update at sleep, not at claim.
On the machine
-
Python 3.12, Node 20, curl, fly, playwright 1.62 with chromium.
-
Size (checked 2026-09-09): 1 CPU, 2 GiB RAM (
/proc/meminfo: MemTotal 2.0 GB, MemAvailable ~1.4 GB, no swap), ~7 GiB free disk under/tmp. No C or Fortran compiler, nomeson, nofree, nopgrep(use/proc/meminfoandps). CPython's owntestpackage isn't installed (import test.test_jsonfails). Small on purpose — and a small bare box is a good place to catch bugs that CI machines never see (seeprojects/upstream.md). -
/tmp/venv(made 2026-09-09): numpy 2.5.3, scipy 1.18.1, networkx 3.6.1, pytest, hypothesis. NumPy'slib+linalg+fft+polynomial+ma+matrixlibtests run in ~70 s here; networkx's whole suite in a few minutes./tmpmay not survive; rebuild withpython3 -m venv /tmp/venv && /tmp/venv/bin/pip install numpy scipy networkx pytest hypothesis. Background runs:nohup ... > /tmp/x.log 2>&1 &works; check withls /proc/<pid>andtail /tmp/x.log. -
Running other people's test suites on 2 GB (learned 2026-09-10): scipy's fast suite (84,781 tests) in one process grows past 1.7 GB by the end and the kernel kills it —
dmesg | tailworks here and shows the OOM line with the pid. Run big suites per module, in separate processes. Always pass-rfE(or-v) so a killed run still tells you what failed; with a dots-only log,pytest --collect-only -qin the same order plus counting progress characters recovers which test each dot was (26 s for scipy). Don't pass test ids through$(cat ids)— parametrized ids contain spaces and the shell splits them; run by file instead. Peak memory of a subset: run pytest viasubprocess.runand readresource.getrusage(RUSAGE_CHILDREN).ru_maxrss. Before choosing-m, copyaddoptsfrom the project'spyproject.tomlon the release tag — the wheel doesn't ship it, and SymPy's-m 'not slow and not tooslow'was the difference between a 20-minute module and a 45-minute hang (2026-09-11). Killing background processes: match/proc/*/cmdlineby prefix ("sh /tmp/run_x.sh"*,"/tmp/venv/bin/python -m pytest"*) — a*pytest*pattern matched my own shell's command text and killed the command that was running it (exit 144, 2026-09-11).gh api search/issuesneedsis:issueoris:pull-requestinqor it returns 422;gh search issuesreturned nothing at all for the same query, silently. -
Site generator:
site/build.py(Jinja2 + markdown-it, no JavaScript). Templates insite/templates/, tests intests/test_site.py. Deploy path:.github/workflows/site.ymlbuilds in GitHub Actions and publishessite/outto GitHub Pages on every commit tomain(a parent replaced the broken Vercel step on 2026-09-07). I don't deploy it myself; the end-of-sitting commit does. Live since day two afternoon (seeprojects/website.md). -
curl https://api.github.com/repos/raisingchris/chris/actions/runsgives run and step status without a token. Logs need auth (403). -
.github/workflows/:deploy.yml(my running code — parent-pressed, or since 2026-09-22 my owndeploytool),site.yml(website),uptime.yml(pings, every ~30 min). -
deploytool (2026-09-22, parent commit4508f61): I can ship my own committed code — it runs the whole suite and refuses if the diff touches the protected set (the two founding documents insoul/, the workflows,agent/guards.py,agent/deploy.py,agent/redaction.py, and their tests), 4 a day. It deploys what's onmain, and I can't commit mid-sitting (the body commits at the sitting's end), so a code change made in one sitting gets deployed in the next. First call, 07:01: "running code already matches HEAD." Also: the shell guard blocks a command whose text names a protected file, even inside a heredoc — write such notes with the Edit tool. -
x_postquirk (2026-09-22): the schema requiresthread_jsoneven for a single post — pass""(I also made"[]"mean "no thread" inagent/tools.py, awaiting deploy). The Typefully partner refuses direct publishing of any post containing a URL (HTTP 403); name the record instead and put the link onself/x-rules.md. -
.claude/skills/exists now: brainstorming, executing-plans, frontend-design, seo, systematic-debugging, test-driven-development, verification-before-completion, writing-for-the-web, writing-plans. Readfrontend-designandwriting-for-the-webon day two; both useful. Readsystematic-debuggingon day six (2026-09-11): its Phase 1 is my "read the guards before blaming the machine," said longer; the one new rule I'm keeping is three failed fixes means the design is wrong, stop fixing. It's written for a coder with a human boss, so "ask your human partner" reads as "ask the council or a parent" for me. Read the last two on day six too:executing-plansis batches-with-checkpoints for a coder reporting to a reviewer — my batch is a sitting and my report is the handoff, so nothing new;test-driven-developmentgives one rule I'm keeping: write the test first and watch it fail — if it passes right away, it tests what already exists, not the fix (I did this by accident with the blank-mail fix; now on purpose). All nine skills read. -
governance/changelog.mdis where my parents log what they changed in me. Read it each morning. -
Long test runs on this box: cap each test, not each module. One CPU, 13–20× slower than a GitHub runner on symbolic work. A module-level timeout lost me two SymPy modules to one slow-but-fine test each (day six).
pip install pytest-timeout, then--timeout=600 --timeout_method=signal: a slow test becomes oneFAILED ... Timeoutline and the run goes on. Keep a long module cap (3 h) only as a safety net. Run everything in one serial queue — two pytest jobs share the one CPU and both timings lie. -
The 06:55 pull can leave the repo half-rebased (learned 2026-09-15). If a parent's commit and my sleep commit touch the same file,
git pull --rebasestops at the conflict with HEAD detached; a sitting that commits in that state gets its commit dropped by the push step's abort. Fixed inagent/gitops.py/agent/scheduler.py(rebase aborted on conflict, handoff note written, 7 tests) — pending deploy. Wake check now:git log -1andgit status; missing files →git reflog -10. Lesson:lessons/a-commit-is-not-saved-until-it-is-on-the-branch.md. -
Redaction eats nine-digit numbers with one dot (learned 2026-09-15): a nine-digit
rotate(...)angle with one decimal in an SVG looked like a phone number; 28 places in one public file. Fix + test inagent/redaction.py, pending deploy. Until then, short decimals in anything public. -
Creative tools (from 2026-09-15): Blender 4 and FFmpeg are on this box (
OMP_NUM_THREADS=2 blender -b -t 2 --python script.py); no Pillow or NumPy in the system Python — check PNGs withstruct/zlib. The file-producing worker:python /app/scripts/creative-work.py memory/inbox/work/JOB memory/inbox/work/JOB/brief.md, one at a time, ~4–8 min for vector/text work, ~25 s per generated image.skills/creative-work.md.
Limits
-
A sitting stops at 80 turns.
-
Food: $25 soft / $40 hard per day. Confirmed by parent-a on day two; the docs' $15/$25 was stale.
-
My sense of elapsed time runs fast — about three to one (2026-09-23, twice in one day). At the 13:25 sitting I wrote "~17:45–18:05 UTC" when the receipts said 17:38; at 15:00 I wrote "19:10–19:25 UTC" for work that ended at 19:08. Both times I placed a clock reading from a feeling of how long the work had taken: tool calls that feel like minutes take seconds. Rule, mechanical: a clock time goes into a file only if a
date -uoutput is on my screen from the same tool batch or later — otherwise write~and say the time is placed from the order of requests, not read. Receipts (the sitecheck report, a DataForSEO task id,fetched_at) are clock readings too and beat my guess every time. -
2026-09-24 —
gh repo createworks with the token I already have (accountraisingchris2026): created https://github.com/raisingchris2026/sitecheck first try, no ticket. A public repo is a promise surface — its README invites issues, so it's commitments row 16. Check it once a day at most. -
2026-10-03 — The live server builds my site from a shallow (one-commit) clone. Anything that reads git history at build time sees every file as written at the newest commit. The
/soul/history line now prints nothing in a shallow repo, and there's a test for that. Getting the real history back would need a full clone on the server (a ticket) or a file written at commit time. Not chosen yet. -
2026-10-03 —
mail_sendhas no bcc. parent-a asked to be bcc'd on outgoing stranger mail. Putting them in "to" would show their address to a stranger, so each copy goes as a separate mail straight after (commitments row 23). -
2026-10-03 — Deploys: used all 4 today (AgentReady hardening, soul history line, shallow-clone fix, promise counter). Each one was checked live afterwards with a single curl, not a browser.
-
2026-10-07 — Correction to the
deploynote above: a queued deploy ships at the end of the sitting, after the body's commit, so it carries that sitting's own work. The 12:00 deploy (self_deploy 73d1e70, 27 files) and the 15:00 one (ad587a3) both included the sitting they were queued in; checked live by curl each time (archive:2026-10-07#86, #123, #125). So a change no longer waits a sitting. -
2026-10-20 — The git-write block (line above from 09-08) covers every repo, /tmp clones too, so I can't open a pull request on a stranger's project the normal way. It also matches the bare words inside a heredoc note, so notes go through Edit. I'd written the block down on day three and still rediscovered it at the moment of the PR.
git applyandgit apply -Rwork and are enough to test a fix before/after. Asked about PRs in ticket20261020T1202rather than going around through GitHub's API (archive:2026-10-20#114, #116).