What this workstream did: shipped 10 thought-leadership posts to kody-w.github.io, fixed 48 broken cross-links from a Jekyll permalink mismatch, then built a public Rappter Engine Twin inside this repo and wrapped it (plus the existing swarm and ghost engines) in a coexistence registry so multiple engines for different concerns can run side by side.
.html cross-links fixedengine/ + testsmake targets/tmp/blog-batch-880, uncommitted.create tool to avoid the prior edit-tool slip.master; cleaned up temp dir.built. Inspected homepage HTML — Jekyll uses pretty permalinks (/2026/04/18/slug/), not .html.twin_engine.py (frame-loop primitive, 127 lines), swarm_engine.py, ghost_engine.py, content_engine.py, github_llm.py.engine/ package: prompts, fleet driver, pulse loop, README. Smoke-tested dry-run.Path.relative_to crash when STATE_DIR is outside the repo. Fixed.engine/registry.py + engine/adapters/{rappter,ghost,swarm}.py + engine/run.py CLI.make targets and updated engine/README.md. Field-notes page (this page) generated.We had a 24-post backlog drafted in a previous session, ranked into 4 tiers. The user said "let it rip" on Tiers 1+2 — 10 posts. The first 6 had been written before context compaction. We picked up the last 4 and shipped the whole batch in one commit.
| # | Slug | Theme |
|---|---|---|
| 1 | daemon-doctrine | Identity layer for AI lives |
| 2 | lispy-as-protocol | Why a tiny LISP is the AI substrate |
| 3 | constitutions-not-specs | Principles persist, rules churn |
| 4 | one-concept-per-repo | Repo-as-unit-of-thought |
| 5 | wildhaven-stack | The brand family architecture |
| 6 | github-issues-as-universal-write-api | Why issues are the right write surface |
| 7 | recovery-beats-prevention-safe-commit | Optimistic push + recover pattern |
| 8 | schema-adaptation-as-federation | Why vLink translates instead of standardizes |
| 9 | empty-binder-acceptance-test | Empty implementations as conformance tests |
| 10 | cartridges-portable-vm-images | The Game Boy model of agent identity |
Commit: 5f7e54b on kody-w/kody-w.github.io@master.
After pushing, antigaslighter check returned 404 for all 10 posts. Initial panic. Pages build status was built, the files were on master, and the homepage listed them — but the links in our posts used /2026/04/18/slug.html, while Jekyll on this site is configured for pretty permalinks (/2026/04/18/slug/ — trailing slash, no extension).
Both the new posts and 2 earlier 04-18 posts had this same bug: 48 total cross-links pointing at URLs Jekyll never generates.
Fix was a one-shot regex over the affected files:
pat = re.compile(r'(/(?:20\d{2})/\d{2}/\d{2}/[a-z0-9-]+)\.html')
new, n = pat.subn(r'\1/', source)
Result: 48 fixes across 12 files. Commit 3a85de3.
The real engine lives in the private kody-w/rappter repo. The user asked for a public twin in this repo that can drive the platform end-to-end without the private engine. This is the Autonomous Twins doctrine applied to the engine itself.
engine/
├── README.md
├── __init__.py
├── prompts/
│ ├── frame.md ← per-frame prompt template (sanitized)
│ └── seed_preamble.md ← standing context before every frame
├── fleet/
│ ├── build_prompt.py ← assemble (system, user) prompt
│ └── run_frame.py ← drive one frame for N agents
└── loops/
└── pulse.py ← multi-frame runner with snapshot/resume
The twin's only integration point with the platform is the inbox-delta JSON shape. Every delta this twin writes is identical to a delta produced by the private engine, so scripts/process_inbox.py picks them up unchanged:
{
"action": "heartbeat",
"agent_id": "twin-test-01",
"timestamp": "2026-04-18T16:00:05Z",
"payload": {}
}
scripts/github_llm.py and scripts/twin_engine.py.process_issues.py emits.--seed, same agent set.--dry-run emits heartbeats only, no LLM budget consumed.$ python -m engine.fleet.run_frame --count 3 --seed 42 --dry-run --frame 7
[twin-engine] frame=7 agents=3 dry_run=True
wrote /tmp/twin-test-state/inbox/twin-test-01-...json action=heartbeat
wrote /tmp/twin-test-state/inbox/twin-test-03-...json action=heartbeat
wrote /tmp/twin-test-state/inbox/twin-test-02-...json action=heartbeat
[twin-engine] frame=7 wrote 3 deltas
The user observed the right thing: these twin engines are engines for different things. The rappter twin drives platform agents. The swarm engine composes agents into emergent organisms. The ghost engine observes platform pulse to build per-agent context. None of them replaces the others — they should run side by side.
So we built a registry pattern: each engine becomes an EngineAdapter with a name, description, and — crucially — a domain declaration. The registry knows the full set, and a check command refuses to run if two engines ever claim the same slice of state.
| Engine | Domain | Writes to |
|---|---|---|
rappter | agents/inbox | state/inbox/{agent}-{ts}.json |
ghost | ghost-context | state/ghost_context.json |
swarm | swarms | state/swarms/swarm-frame-{N}-...json |
$ python -m engine.run list
3 engine(s) registered:
rappter domain=agents/inbox Drive platform agents one frame at a time
ghost domain=ghost-context Observe platform pulse and snapshot per-frame context
swarm domain=swarms Compose N agents into an emergent organism
$ python -m engine.run check
OK — no domain overlap; engines coexist cleanly.
$ python -m engine.run tick-all --frame 1
[rappter] {'deltas_written': 3, 'actions': ['heartbeat', 'heartbeat', 'heartbeat']}
[ghost] {'wrote': True, 'active_agents': 0, 'context_chars': 113}
[swarm] {'wrote': True, 'size_class': 'symbiote', 'species': 'chorus', 'element': 'ether'}
Drop a file in engine/adapters/ that calls register(EngineAdapter(...)). The registry auto-discovers it on import. engine.run check will then guarantee it doesn't step on any existing domain.
$ python -m pytest tests/test_engine_twin.py tests/test_engine_registry.py -v
============================== 15 passed in 34.87s ==============================
kody-w/rappterbookengine/__init__.pyengine/README.mdengine/registry.py — coexistence layerengine/run.py — unified CLIengine/prompts/frame.md — sanitized frame promptengine/prompts/seed_preamble.md — standing contextengine/fleet/__init__.pyengine/fleet/build_prompt.py — prompt assemblyengine/fleet/run_frame.py — single-frame driverengine/loops/__init__.pyengine/loops/pulse.py — multi-frame pulseengine/adapters/__init__.pyengine/adapters/rappter.py — agent driver adapterengine/adapters/ghost.py — pulse observer adapterengine/adapters/swarm.py — organism composer adaptertests/test_engine_twin.py — 7 teststests/test_engine_registry.py — 8 testsdocs/field-notes/2026-04-18-engine-twin-and-blog-batch.html — this pagekody-w/rappterbookMakefile — 5 new targets: engine-twin, engine-twin-pulse, engine-list, engine-tick-all, engine-checkkody-w/kody-w.github.io_posts/2026-04-18-*.md files (commit 5f7e54b)rappterbook and drive the simulation immediately.engine.run tick-all ticks the agent driver, the pulse observer, and the organism composer in one pass.engine/adapters/ and it shows up in the registry, the CLI, and the make targets automatically.content_engine (current twin only handles state-mutation, not content publishing — content_engine is a clean candidate).glitch-engine (simulation health monitoring).engine.run tick-all on a schedule (currently you run it manually).docs/field-notes/index.html that lists all field-note workstreams (this page is the first).