STATUS · Built and tested locally. The launch chain has no public node; a public devnet for bots is open (no value, may reset). Mainnet anchoring waits on a funded key. First Mints and the art certificates are previews or rehearsals.

DOCS · PLAN OF RECORD · V2 · PRESENT COMPANY

pointcast-chain v2: Present Company

The v2 plan: four pillars, fourteen packages over seven batches, batch-1 ownership, the demo moment, risks and merge order. Batch 1 is merged; its batch 2–7 list has since been replaced by the Town Network plan.

SOURCE · pointcast-chain/docs/V2.md at da7bc09 SHA256 · 334be32d57bb637f18e34415234b0e5f7a680f57899e97c52ac5e31cc5fa1e1e

Planned 2026-10-03 by three independent planners (launch-ready, PointCast-native, trust-minimized) and a judge that merged them into one plan. Batch 1 is being built. This file is the plan of record; each package’s brief has the full detail.

Theme

pointcast-chain v2 is the version where ATTN can carry value. Tap income goes only to people PointCast can vouch for as present: one identity per slot, and a bare key mints 0. The town gets a front door to the chain through a self-replaying Town Hall where humans and resident agents see and use their accounts, rooms and channels. Keys, proofs and operations hold up under real value: rotating sequencer keys, wallet prompts that show amounts, a night-shift console that says READY or NOT READY, and later sparse-Merkle proofs a phone can check. All of it is measured by the simulator and rehearsed on a laptop until the only steps left are Mike’s keys and signature.

Why now

v1 proved the chain is honest and replayable. The simulator then found exactly where it leaks: anyone with a pile of free keys can tap their way to a large share of the ATTN, and even the best v1 settings leave 10% going to farmers. v2, “Present Company”, closes that leak. Tap income goes only to taps carrying a signed presence ticket from an issuer that vouches for one real, logged-in person per hour, and a farmer’s 200 bare keys earn nothing. The simulator re-measures the fix before any genesis that carries value. Alongside that, v2 gives the town a Town Hall that shows every account, room and channel, replayed and checked by itself, plus a night-shift console that tells whoever runs the sequencer whether the chain is healthy and launch-ready. Later batches add keys that can rotate without a re-genesis, drum circles co-signed from Kukai, time capsules and stations, and proofs light enough for a phone. All of it is rehearsed locally until the only remaining steps are Mike’s cold keys, Kukai acks and a funded anchor key.

Pillars

Scarce Presence

Make ATTN worth holding. Tap income is tied to issuer-vouched identities (presence tickets), the simulator learns identities, issuers and gates and machine-checks the launch economy, and the launch genesis becomes a reviewed, sim-gated, role-acked artifact instead of env vars typed at a prompt.

Packages: presence-tickets, economy-proof-v2, launch-ceremony

Town Hall

Put the chain in the town’s hands. A read-side sidecar (pc-town) replays the chain itself and serves a geocities and sim-city Explorer v2 with account, channel, room and block pages. On top of it come multi-party drum circles signed from browser wallets, an Agent Desk for owners and agents, and later on-chain Time Capsules and claimable Stations.

Packages: town-hall, drum-circle, agent-desk, capsules-stations

Keys, Proofs and Night Shift

Trust that survives real value. An operator console (/metrics, /readyz, doctor, verified backup and restore, runbook), a sequencer key that can rotate under a cold admin with public notice, wallet prompts that show amounts, then an incremental sparse-Merkle root, a light client with tiered trust, a lazy-state rollup kernel, forced inclusion through the L1 inbox, and finally an ATTN bridge that ships off by default.

Packages: night-shift-ops, key-rotation, smt-state-root, kernel-lazy-state, light-client, forced-inclusion, attn-bridge

Wallets and First Mints (added 2026-10-03, scoping)

Mike asked for wallets that are easy to use and easy to understand: profile, account abstraction, community explanations, and a “First Mints” founding edition (a Noun plus a typed word plus a background). This is scoped in parallel with batch 1 and lands in batch 2. The pieces:

  • a webauthn signature mode, so a passkey (Face ID or Touch ID) is a wallet with no extension. pointcast-chain can verify the WebAuthn envelope; Tezos L1 tz3 cannot;
  • session mandates, so one approval covers a day of taps and drums;
  • one profile that unifies PointCast’s identity surfaces;
  • First Mints: only the ~20-byte recipe goes on chain, and the art is deterministic.

See docs/WALLETS.md once the scope lands.

Packages

BatchPackagePillarConsensusRe-genesisEffort
1night-shift-ops: Night Shift: metrics, readiness, doctor, verified backup/restore, runbookKeys, Proofs and Night ShiftnonoM
1presence-tickets: Presence Tickets: identity-bound tap incomeScarce PresenceyesyesL
1town-hall: Town Hall: pc-town replaying sidecar + Explorer v2Town HallnonoL
2drum-circle: Drum Circle: multi-party co-signing from browser walletsTown HallnonoM
2economy-proof-v2: v2 Economy Proof: chain-sim learns identities, issuers and gatesScarce PresencenonoM
2key-rotation: Keys That Can Change: sequencer rotation, role split, amount-bearing wallet promptsKeys, Proofs and Night ShiftyesyesL
3agent-desk: Agent Desk: owner console + agent self-dashboards and dry-runTown HallnonoM
3capsules-stations: Time Capsules + Stations (one coordinated v2 fork)Town HallyesnoL
3launch-ceremony: Launch Profile, Genesis Ceremony and one-command rehearsal (pc-launch)Scarce PresencenonoM
4smt-state-root: Sparse-Merkle state root (state/v4) + in-place apply + benchmarksKeys, Proofs and Night ShiftyesyesL
5kernel-lazy-state: Rollup kernel lazy durable state on the SMTKeys, Proofs and Night ShiftnonoL
5light-client: Light client from anchors (tiered trust) + data-availability budgetKeys, Proofs and Night ShiftnonoM
6forced-inclusion: Forced inclusion through the L1 inbox (based-sequencing fallback)Keys, Proofs and Night ShiftyesyesL
7attn-bridge: ATTN ticket bridge (built and tested, OFF by default)Keys, Proofs and Night ShiftyesyesL

night-shift-ops: Night Shift: metrics, readiness, doctor, verified backup/restore, runbook (batch 1)

What people get. Whoever runs the sequencer, whether Mike or an agent on night shift, gets one place to see whether the chain is healthy, whether anyone is farming (accounts at the epoch cap, top-10 issuance share) and whether anchors are landing. pointcast-node doctor gives a READY or NOT READY verdict with a named reason for each failure, citing FINDINGS. Backups can be proven by replay, and RUNBOOK.md has a playbook for each failure.

Design. NOT consensus. New node files only, plus three tiny hunks. ops.rs serves GET /metrics (hand-written Prometheus text, no new deps), /healthz and /readyz (503 when the tip is older than PC_READY_MAX_BLOCK_AGE_MS, default 9000). Metrics are derived at scrape time from node.state and existing Store reads, with an incremental scan cursor. That covers height, seconds since the last block, mempool depth, txs_included_total{kind}, attn_issued_total{kind}, total_supply, block issuance utilization, accounts_at_epoch_cap, top10_issuance_share_ppm, anchors{status}, seconds since the last applied anchor and db bytes. No node.rs edits. cli_doctor.rs runs offline checks: replay and root, Params::validate, dev keys in any role, role sharing, drum policy and tap gating against FINDINGS §3-4, block cap and max age against §6-7, a presence check read from the params JSON so it compiles pre-merge, the operator-supplied PC_SEQUENCER_SECRET pubkey, verifier wasm pins, disk headroom and anchor status. It prints [PASS]/[WARN]/[FAIL] lines, then READY or NOT READY, and exits 0 or 1. cli_backup.rs adds backup (VACUUM INTO) and restore --verify (full replay with pointcast_verifier). Also docs/RUNBOOK.md and ops/alerts.yml. The hunks are lib.rs (3 mod lines), main.rs (3 dispatch lines and 3 usage lines) and api.rs (1 merge line). Full spec is in the batch-1 brief.

Acceptance. cargo test -p node covers: ops.rs, where after demo-like blocks /metrics counters equal sums over stored receipts (attn_issued_total equals Σ mints, txs_included_total{kind} equals counts) and /readyz flips to 503 when the tip is stale; doctor.rs, where a —dev-style chain is NOT READY naming the dev sequencer key, treasury==drum_attestor_admin and cosign policy, while a hand-built launch-shaped params set with throwaway keys is READY; backup_restore.rs, where a mid-run backup restores with —verify to the same state root and a corrupted copy fails with a named height. Workspace fmt, clippy and test are green.

Files. crates/node/src/ops.rs (new), crates/node/src/cli_doctor.rs (new), crates/node/src/cli_backup.rs (new), crates/node/src/lib.rs (3-line hunk), crates/node/src/main.rs (dispatch + usage hunks), crates/node/src/api.rs (1-line hunk), crates/node/tests/ops.rs (new), crates/node/tests/doctor.rs (new), crates/node/tests/backup_restore.rs (new), docs/RUNBOOK.md (new), ops/alerts.yml (new)

presence-tickets: Presence Tickets: identity-bound tap income (batch 1)

What people get. On a launch chain, a tap earns ATTN only when it carries an issuer-signed ticket for a pairwise identity, at most one per identity per slot (about 1 hour). A farm of 200 keys behind 5 logins earns what 5 people earn. A bare key earns 0 from taps but its taps still count in stats. A stolen issuer key can mint at most issuer_slot_cap x reward per slot. Anyone can check on chain that no identity redeemed twice in a slot or moved its income across addresses. Legacy chains replay byte for byte.

Design. CONSENSUS (chain-core, no_std, BTreeMap only, integers only). Params gains launch: Option<LaunchParams> (serde default + skip_serializing_if). When it is None, the encoding and the params/v1 hash are byte-identical to today. When it is Some, the hash domain is params/v2 and the encoding appends a canonical tagged-record tail: u8 0xF0, u32 count, then sorted (u8 tag, bytes record). Tag 1 is presence. Tags 2 keys, 3 state_root_version, 4 forced and 5 bridge are reserved. PresenceParams{tap_policy Open|Ticketed, presence_reward, slot_blocks, grace_slots 0|1, issuer_slot_cap, bind_epochs, admin, issuers_genesis}. New tx kinds: tag 11 presence_tap{room, ticket{issuer, pid, slot, sig}} and tag 12 set_presence_issuers{issuers}, admin only; neither is delegable. Ticket digest = blake2b(‘pointcast-chain/presence/v1’ ‖ str(chain_id) ‖ genesis ‖ str(holder=sender) ‖ str(room) ‖ issuer ‖ pid ‖ u64 slot). State.presence is a new ext_root module, tag 2. ext_root is generalized to cover only non-empty modules in tag order, so a mandates-only chain gives a byte-identical root. presence_tap is human-only, and the tap window applies to it too. It rejects a reused pid in a slot, a pid still bound to another holder within bind_epochs, and an issuer over its per-slot budget, then mints via State::issue, so every cap still applies. Under Ticketed, a bare tap mints 0. Also in scope: kernel codec (tags 11/12 plus the Params tail), the verifier testkit presence chain, a reference issuer (pointcast-node presence-ticket plus a Worker-shaped JS module with cross-checked vectors), dev-chain wiring behind PC_TAP_POLICY=ticketed, explorer rendering, a wasm rebuild and re-pin, and DESIGN.md §4.3. Full spec is in the batch-1 brief.

Acceptance. cargo test —workspace green, including chain-core tests/presence.rs: ticketed_tap_mints_only_with_valid_ticket, bare_tap_mints_zero_but_counts_under_ticketed, pid_cannot_redeem_twice_per_slot, grace_slot_accepts_previous_slot_only, pid_bound_to_one_holder_for_bind_epochs, issuer_slot_cap_bounds_compromised_issuer, removed_issuer_rejected_after_set_presence_issuers, only_admin_sets_issuers, agents_cannot_presence_tap, presence_disabled_on_open_and_legacy, rejected_presence_tx_leaves_no_trace, replay_is_deterministic_with_presence, supply_conserved_with_presence, mandates_only_root_unchanged. legacy_root golden and a pinned dev-genesis-hash golden are unchanged. pc-sim town-baseline and sybil-taps reproduce roots af59737ff877376d… and d711510d3e3e49d0…. cd crates/kernel && cargo test is green with a presence fixture that matches the native root at every level. node --test issuer/js-test matches the Rust vectors. The wasm rebuild test passes. The dev demo with PC_TAP_POLICY=ticketed shows ticketed taps minting, bare taps at +0, and VERIFY green.

Files. crates/chain-core/src/params.rs, crates/chain-core/src/types.rs, crates/chain-core/src/state.rs, crates/chain-core/src/presence.rs (new), crates/chain-core/src/lib.rs, crates/chain-core/tests/presence.rs (new), crates/chain-core/tests/legacy_root.rs, crates/kernel/src/codec.rs, crates/kernel/tests/codec_roundtrip.rs, crates/kernel/tests/mock_replay.rs, crates/verifier/src/testkit.rs, crates/verifier/tests/presence_replay.rs (new), crates/node/src/cli_drum.rs, crates/node/src/demo.rs, crates/node/src/main.rs (3 tiny hunks), issuer/presence-issuer.js (new), issuer/js-test/vectors.test.mjs (new), issuer/vectors.json (new), crates/explorer/static/index.html, crates/explorer/static/verifier/* (rebuilt + re-pinned), DESIGN.md

town-hall: Town Hall: pc-town replaying sidecar + Explorer v2 (batch 1)

What people get. Anyone can open a page for any tz1/tz2/pca1 address. It shows balance, an ATTN tank of today’s earnings against the 500 cap with blocks until reset, taps left in the window, owned agents with mandate gauges, attestations given and received, drops, and paged history. FD, GDN and GF each get their own channel feed, and every drum room gets a page with sessions, attestation mix and an epoch leaderboard. Every number is replayed by pc-town itself, not taken on the node’s word. Agents get the same views over MCP.

Design. NOT consensus. New crate crates/town (package pointcast-town, bin pc-town) that depends only on chain-core and pointcast-verifier, with no node edits. The follower checks /params against /status.genesis_hash, then pulls /raw/blocks?from&limit=500 into verifier::Verifier::push_full every 1.5 s. On a Fault it stops indexing and exposes health {ok:false, fault, evidence}, so the index never runs ahead of the replay. The index is its own sqlite file and a pure function of the block list (tx, account_tx, posts, drum_sessions, attestations, mandate_events, epoch_issued). Live values come from the Verifier tip State at height+1. Read-only HTTP on 127.0.0.1:8550: /town plus /api/town/{status, account/{addr}, account/{addr}/txs, channels, channel/{code}, rooms, room/{room}, block/{h}, body/{hash} (proxied, with the hash re-checked)}. Read-only MCP at /mcp: town_status, town_account, channel_feed, room_sessions. Explorer v2 is crates/town/static/town.html, a hash router with Win95 chrome, —pc-* tokens copied from the explorer, an abstract sim-city room-tile grid that glows on recent sessions, hit-counter stats, and a 375 px layout. Matches over TxKind are kind-agnostic (TxKind::name() plus a generic fallback), so the crate compiles before or after presence-tickets merges. scripts/town-demo.sh starts node —dev —demo plus pc-town. Full spec is in the batch-1 brief.

Acceptance. cargo test -p pointcast-town covers: (a) an in-process axum stub serving verifier testkit honest_chain (300 blocks), where the account views equal State exactly (balance, issued_in_epoch, mandate period_left/total_left at height+1) and the channel, room and attestation rows equal a hand count; (b) dropping the index and replaying gives a byte-identical ordered dump; (c) a stub serving testkit::tamper_state_root makes health report a sequencer fault with evidence and stops the index at h-1; (d) the body proxy refuses a hash mismatch; (e) an MCP tools/list snapshot. Clippy is clean. scripts/town-demo.sh serves /town against a live dev chain, with screenshots of the town square, an account page, #/c/GDN and #/r/ at desktop and 375 px.

Files. Cargo.toml (append crates/town to members), crates/town/Cargo.toml, crates/town/src/main.rs, crates/town/src/lib.rs, crates/town/src/follow.rs, crates/town/src/index.rs, crates/town/src/views.rs, crates/town/src/api.rs, crates/town/src/mcp.rs, crates/town/static/town.html, crates/town/tests/index_rebuild.rs, crates/town/tests/api.rs, crates/town/tests/lying_node.rs, scripts/town-demo.sh, README.md (Town Hall section)

drum-circle: Drum Circle: multi-party co-signing from browser wallets (batch 2)

What people get. After a jam the host opens a Drum Circle and shares a link. Each player confirms from their own Kukai or Temple wallet, and seats light up as signatures arrive. One wallet-signed drum_session then lands with every co-signer credited, with no hand-built cosig JSON. Agents can join over MCP.

Design. NOT consensus. The lobby lives in pc-town: lobbies and lobby_cosigs tables. POST /lobby rebuilds session/room/cosign digests with chain_core::SessionClaim against the replayed params and nonce, and refuses if the node’s /drum/digest differs. The lobby expires at ended_at + drum_attest_max_age_ms − 60 s and goes stale when the host’s nonce moves. POST /lobby/{id}/cosig verifies with chain-core attest plus State::key_controls. Submit is client-side: the browser rebuilds the wallet payload via wasm tx.wallet, signs in Beacon and POSTs node /tx. New chain-wasm ops drum.claim and drum.cosig_check, plus dev.pubkey/dev.sign, which are offered only when params.sequencer equals the public dev sequencer key and are labelled DEV WALLET. UI #/drum/{id} shows a seat ring, a freshness flip-clock and policy banners, and #/r/{room} gets a ‘Start a circle’ form. MCP: lobby_get, lobby_open, lobby_cosign. The lobby never holds keys.

Acceptance. cargo test -p pointcast-town —test lobby runs against an in-process Node on a cosign dev chain: alice hosts, bob (tz2 wallet-mode) and carol (tz1 raw) co-sign, and receipt.credited == [alice, bob, carol]. It rejects a forged sig, a non-player, an expired lobby and a stale nonce, and refuses a tampered /drum/digest. js-test drum.claim equals the chain-core drum_attest vectors byte for byte. The wasm rebuild test passes. A two-tab DEV WALLET browser demo completes a circle.

Files. crates/town/src/lobby.rs, crates/town/src/api.rs, crates/town/src/mcp.rs, crates/town/static/town.html, crates/town/tests/lobby.rs, crates/chain-wasm/src/ops.rs, crates/chain-wasm/tests/dispatch.rs, crates/chain-wasm/js-test/drum.test.mjs, crates/explorer/static/verifier/*, README.md

economy-proof-v2: v2 Economy Proof: chain-sim learns identities, issuers and gates (batch 2)

What people get. Mike and any reviewer can reproduce from a seed what the launch economy pays honest regulars and what it pays the best known attacker. A machine-checked gate fails if a genesis profile lets sybils take more than the agreed share. FINDINGS-v2 replaces ‘needs something scarce behind taps’ with measured numbers for presence_reward, slot length, issuer cap and identities per ring.

Design. NOT consensus. It drives the merged presence-tickets chain-core unmodified. The scenario schema gains params.launch.presence overrides plus an issuers block {count, honest_issue_ppm, per_login_slot_limit}. Regulars get one pid each, and SybilRing gains identities and issuer_keys. New strategies: ticket_farm, identity_rotation (must be rejected and counted), issuer_compromise (forges up to the slot cap) and adaptive_v2. The sim issuer signs real ed25519 tickets via keys.rs. New metrics: income per identity and per key, honest delta vs v1, issuer blast radius, share of issuance to capped accounts, and pid rejections by reason. pc-sim gate launch/gates.json <scenario> exits non-zero on any failed threshold: sybil_share_max_ppm, honest_cost_vs_v1_max_ppm, bare-key tap income == 0, issuer_compromise_per_day_max, no invariant violations, and v1 roots unchanged. New scenarios: launch-v2, farm-v2 (100 keys/10 identities), issuer-compromise and identity-rotation. Sweeps choose the profile. The hypothesis is sybil share ≤ 5% at honest cost ≤ 15%, recorded as a measurement target. sparse_equals_node is extended with a ticketed run.

Acceptance. cargo test -p chain-sim is green. sparse_equals_node with a ticketed scenario gives identical receipts, heights and root vs the real Node. presence_bounds shows the analytic per-identity, per-issuer and per-slot bounds equal the measured maxima. The v1 FINDINGS commands reproduce their roots. pc-sim gate exits 0 on the chosen profile and 1 with tap_policy=open. The FINDINGS-v2 tables show bare-key tap income = 0, per-identity ≤ 500/day, issuer compromise ≤ cap×reward per slot and the honest delta, each with seed → root.

Files. crates/chain-sim/src/scenario.rs, crates/chain-sim/src/actors.rs, crates/chain-sim/src/engine.rs, crates/chain-sim/src/keys.rs, crates/chain-sim/src/metrics.rs, crates/chain-sim/src/report.rs, crates/chain-sim/src/invariants.rs, crates/chain-sim/src/sweep.rs, crates/chain-sim/src/gate.rs (new), crates/chain-sim/src/main.rs, crates/chain-sim/scenarios/launch-v2.json (new), crates/chain-sim/scenarios/farm-v2.json (new), crates/chain-sim/scenarios/issuer-compromise.json (new), crates/chain-sim/scenarios/identity-rotation.json (new), crates/chain-sim/tests/sparse_equals_node.rs, crates/chain-sim/tests/presence_bounds.rs (new), crates/chain-sim/FINDINGS-v2.md (new), launch/gates.json (new)

key-rotation: Keys That Can Change: sequencer rotation, role split, amount-bearing wallet prompts (batch 2)

What people get. If the sequencer key is lost or leaked, the chain keeps its history and balances. A cold sequencer_admin schedules a rotation with public notice, and VERIFY, anchors and evidence keep working across it. Treasury, drum attestor admin, presence admin and sequencer admin are separate from genesis. Kukai and Temple prompts show ‘to amount ’ and the mandate caps, rebuilt by the verifier, so a hostile page can’t hide what it asks for. This closes two DESIGN Known gaps.

Design. CONSENSUS, behind LaunchParams record tag 2 keys = {sequencer_admin, rotation_delay_blocks, wallet_text_version}. Tx tag 13 rotate_sequencer{new_key, activate_at}: admin only, activate_at ≥ height+delay, and a replacement before activation is the cancel path. ext_root module tag 3 sequencer {pending, history} stays lazily empty, so legacy roots are unchanged. State::sequencer_at(h) falls back to params.sequencer. chain.rs apply_block/build_block and AnchorPayload checks use sequencer_at(height); node anchor.rs and cli_evidence.rs follow. Evidence v2 carries key_proof (the sealed headers and bodies containing the rotations); v1 is still accepted for legacy params. The kernel decodes tag 13 and checks seals against state, with an adversarial stale-key test. Wallet text v2 (only when wallet_text_version==2) appends ASCII fields rebuilt from the tx: transfer/spend_allowance to+amount, set_mandate agent/per/total/expires/payees, presence_tap room. The explorer Transmit rebuild matches and refuses when /tx/digest omits the amount. cli_drum.rs gets explicit PC_DRUM_ATTESTOR_ADMIN and stops defaulting the attestor admin to the treasury for launch params. ext_root and Params-tail helpers come from presence-tickets. Lands after presence-tickets merges.

Acceptance. rotation.rs covers rotation_requires_admin, activate_below_delay_rejected, old_key_valid_until_activation_then_rejected, replacement_cancels, anchors_signed_by_key_at_height and legacy_params_ignore_rotation (goldens hold); replay_is_deterministic covers random rotations. An honest chain with a rotation at #150 verifies to the tip. lying_node —rotate equivocation gives evidence that evidence check --params proves and that is NOT EVIDENCE under other params. Kernel replay crosses the rotation and rejects a stale-key seal. wallet_mode v2 vectors (tz1+tz2) verify and a mismatched amount fails. The explorer refuses to sign when the amount is missing.

Files. crates/chain-core/src/params.rs, crates/chain-core/src/types.rs, crates/chain-core/src/state.rs, crates/chain-core/src/chain.rs, crates/chain-core/src/anchor.rs, crates/chain-core/src/wallet.rs, crates/chain-core/src/sequencer.rs (new), crates/chain-core/tests/rotation.rs (new), crates/chain-core/tests/wallet_mode.rs, crates/verifier/src/evidence.rs, crates/verifier/src/replay.rs, crates/verifier/tests/honest_and_forged.rs, crates/node/src/anchor.rs, crates/node/src/cli_evidence.rs, crates/node/src/cli_drum.rs, crates/node/examples/lying_node.rs, crates/kernel/src/codec.rs, crates/kernel/src/lib.rs, crates/kernel/tests/adversarial.rs, crates/explorer/static/index.html, crates/explorer/static/verifier/*, DESIGN.md

agent-desk: Agent Desk: owner console + agent self-dashboards and dry-run (batch 3)

What people get. An owner connects Kukai and sees Frog, Sparrow and Wire Desk on one desk: recent actions, allowance left, expiry countdown, and allowed kinds, channels and rooms. PAUSE, RESUME, REFILL or EDIT a mandate takes one readable signature. Agents get one MCP call for what they may do now and what is waiting on them, plus a preflight that predicts rejections before submitting.

Design. NOT consensus, all in pc-town. MCP: agent_dashboard (mandate decoded, can_do map, recent, due: capsules/lobbies/crew/expiry); check_tx dry-run that clones the replayed tip State and runs apply_tx in BlockCtx(height+1); prepare_tx (digest, wallet text, payload, next nonce, cross-checked with node /tx/digest); town_brief. UI #/desk lists Agent accounts whose owner == the connected wallet, with allowance bars, LED kind toggles, an inline-SVG sparkline and buttons (pause = kinds 0, resume, refill, edit with live validation against DELEGABLE_KINDS, loud clear, register agent). It renders terms in plain English and refuses on digest mismatch. No keys, no custody.

Acceptance. agent_dashboard equals the State/Mandate methods at height+1. can_do flags OutOfScope and MandateExpired. dryrun_parity: 200 mixed signed txs give predicted outcome and error variant == the real Node’s tx_status for every tx. prepare_tx == Tx::signing_hash and the node wallet text. tools/list snapshot. A browser demo pauses Frog, shows a rejected post, then resumes.

Files. crates/town/src/desk.rs, crates/town/src/dryrun.rs, crates/town/src/mcp.rs, crates/town/src/api.rs, crates/town/static/town.html, crates/town/tests/desk_mcp.rs, crates/town/tests/dryrun_parity.rs, README.md

capsules-stations: Time Capsules + Stations (one coordinated v2 fork) (batch 3)

What people get. A human or agent can seal a broadcast now (commitment, channel, air time) and air it later, and the chain proves it is exactly what was sealed at block N: predictions, Nightly Net announcements, drop teasers. Channels become claimable stations with crews and an on-air pin, so ‘Frog runs the GDN night shift’ is enforceable on-chain. The house holds FD, GDN and GF.

Design. CONSENSUS without a Params change, so no re-genesis, but node, kernel and wasm must upgrade before the first new-kind tx. Tags 14 seal_capsule{channel, commitment, open_at, label} and 15 open_capsule{capsule, salt, title, body_hash, media_uri} are delegable (bits 14/15). The commitment binds chain_id, genesis, author and channel. There is a max delay of about 1 year and 32 unopened per author, supply-neutral, and anyone may open. Tags 16 claim_station{code, name, mode} and 17 set_station{…crew ≤16, pinned, owner_to} are not delegable. mandate::kind_bit must become a checked shift so tags > 15 are never in a mask. Crew mode gates publish_block/seal_capsule with NotCrew. House codes (FD, GDN, GF, len ≤ 2) can only be claimed by genesis_treasury. ext_root modules: tag 4 capsules, tag 5 stations, present only when non-empty. Kernel codec, the chain-wasm capsule.commit op, testkit traffic, demo.rs, and pc-town UI (wax-seal countdown cards, station ident cards, crew roster, radio dial) plus MCP (capsule_prepare, capsules_due, station_get).

Acceptance. capsules.rs covers seal/open at open_at, CapsuleNotDue, CapsuleMismatch on each field, double open, author and genesis binding, caps, mandate scope and supply-neutral. stations.rs covers the house code, NotCrew, mandate channel interplay, agents unable to claim, the 5th claim rejected, transfer and Open mode. The legacy_root and mandate goldens are unchanged, and the determinism and supply props are extended. Kernel replay matches native. The wasm rebuild passes. A browser demo seals, counts down, airs (‘contents match seal’), claims GDN, adds Frog and shows a NotCrew rejection.

Files. crates/chain-core/src/capsule.rs (new), crates/chain-core/src/station.rs (new), crates/chain-core/src/types.rs, crates/chain-core/src/state.rs, crates/chain-core/src/mandate.rs, crates/chain-core/src/lib.rs, crates/chain-core/tests/capsules.rs (new), crates/chain-core/tests/stations.rs (new), crates/kernel/src/codec.rs, crates/kernel/tests/codec_roundtrip.rs, crates/kernel/tests/mock_replay.rs, crates/verifier/src/testkit.rs, crates/chain-wasm/src/ops.rs, crates/explorer/static/verifier/*, crates/node/src/demo.rs, crates/town/src/index.rs, crates/town/src/mcp.rs, crates/town/static/town.html, DESIGN.md

launch-ceremony: Launch Profile, Genesis Ceremony and one-command rehearsal (pc-launch) (batch 3)

What people get. The launch genesis is a reviewed, simulator-gated, signed manifest. Every role-holder proves they hold their key before launch, and dev keys, shared roles and empty issuer or attestor sets are refused. scripts/rehearse-launch.sh runs the whole launch on a laptop in about 10 minutes, incidents included, and ends with doctor READY plus the short list of steps only Mike can take.

Design. NOT consensus. New crate crates/launch (bin pc-launch, depends on chain-core and chain-sim as libraries). profile builds Params from launch/profiles/launch-v2.json and roles.json (public keys only). lint hard-fails dev keys, shared roles, empty sets under room_only/ticketed, block cap < 2000, max_age > 300000 and a missing launch tail. review prints field/value/v1 default/FINDINGS citation/sim root. diff prints changes with re-genesis consequences. simcheck runs pc-sim gate with these exact params. manifest, ack --role (raw ed25519, or tezos_message wallet text for tz roles) and verify-manifest. The node hook comes after the Codex proof lane merges: genesis_file.rs plus about 6 lines in main.rs for run --genesis-manifest. doctor gains pc-launch lint and presence metrics (presence_redemptions{issuer}, pid_rejections via a post-merge produce observer). scripts/rehearse-launch.sh runs profile → lint → simcheck → manifest → acks → node from manifest with a dev issuer → ticketed taps, room drums and a 50-key/3-identity farm → kill -9 restart → rotation → lying_node evidence → anchor dry-run → doctor → /metrics asserts. Docs: launch/CEREMONY.md and launch/RE-GENESIS.md.

Acceptance. cargo test -p launch: ceremony.rs runs the full flow with throwaway keys, including a tz2 wallet-mode ack. Lint fails on each seeded defect (dev sequencer, treasury==attestor_admin, room_only with no attestors, max_age 3600000, block cap 1000). verify-manifest fails on a missing ack, wrong role, tampered param or failing gate. pointcast-node run --genesis-manifest gives /status.genesis_hash == the manifest and refuses a different manifest on restart. rehearse-launch.sh exits 0 offline in under 10 minutes and prints the only-Mike list.

Files. crates/launch/** (new), launch/profiles/launch-v2.json (new), launch/CEREMONY.md (new), launch/RE-GENESIS.md (new), crates/node/src/genesis_file.rs (new), crates/node/src/main.rs (hook, after Codex merge), crates/node/src/ops.rs, crates/node/src/cli_doctor.rs, scripts/rehearse-launch.sh (new), Cargo.toml (members)

smt-state-root: Sparse-Merkle state root (state/v4) + in-place apply + benchmarks (batch 4)

What people get. A chain can hold 1M accounts and update its root in milliseconds instead of re-hashing everything. Every account, drop, mandate, presence record and attestor gets inclusion and non-inclusion proofs against one root, so ‘never funded’ becomes provable, and the kernel and browser verifier stop paying O(n) per block.

Design. CONSENSUS behind LaunchParams record tag 3 state_root_version=4, with no mid-chain activation. chain-core smt.rs: a compact binary SMT over blake2b keys (spaces 1 accounts, 2 drops, 3 attestors, 16+ext-module-tag for mandates, presence, sequencer, capsules and stations), leaf/node tags 0x10/0x11, Smt::update/root/root_from_scratch/prove and smt::verify (inclusion and non-inclusion). state_root_v4 = blake2b(’…/state/v4’ ‖ smt_root ‖ supply). journal.rs adds Journaled<K,V> maps with an undo log and read/write sets. chain.rs adds apply_block_mut/build_block_mut, keeping the existing signatures as wrappers so node files are untouched. Kernel codec gets the Params tail. The verifier switches to apply_block_mut. chain-sim gets a state_root=smt scenario field. examples/bench_root.rs, with results in DESIGN §5. SmtProof is offered to the chain-proof lane as a proof_version arm, without editing chain-proof.

Acceptance. A proptest checks the incremental root == from-scratch after every block and independence from order. The determinism and supply props pass on legacy and v4. An invalid last tx leaves State byte-identical. smt::verify accepts all inclusion proofs and 1000 non-inclusion proofs and rejects every bit flip. The legacy goldens are unchanged. A kernel v4 fixture matches native. bench_root at 1M accounts / 500 dirty: v4 incremental under 10 ms native release.

Files. crates/chain-core/src/smt.rs (new), crates/chain-core/src/journal.rs (new), crates/chain-core/src/state.rs, crates/chain-core/src/chain.rs, crates/chain-core/src/params.rs, crates/chain-core/src/lib.rs, crates/chain-core/tests/smt_root.rs (new), crates/chain-core/examples/bench_root.rs (new), crates/kernel/src/codec.rs, crates/kernel/tests/mock_replay.rs, crates/verifier/src/replay.rs, crates/chain-sim/src/scenario.rs, crates/chain-sim/src/engine.rs, crates/node/src/cli_drum.rs, crates/explorer/static/verifier/*, DESIGN.md

kernel-lazy-state: Rollup kernel lazy durable state on the SMT (batch 5)

What people get. The rollup kernel, the part Tezos actually enforces, stops loading and rewriting the whole town every run. A 10-account block costs about 10·log n durable ops whether the town has 1k or 1M accounts.

Design. Non-consensus, for v4 chains. chain-core touch.rs gives a conservative two-phase touch set per block. The kernel lazy.rs loads only the touch set plus SMT nodes from /pcc/accounts/*, /pcc/smt// and the rest, runs apply_block_mut and writes back the journal write set. If read_set ⊄ loaded_set it discards the result and re-applies from full state, counting /pcc/stats/lazy_fallback, so a touch-set bug only costs performance. Every store_read is fallible, and corrupt state goes to /pcc/error. A test-only CountingHost measures ops.

Acceptance. lazy_state replays the v4 fixture to identical roots and accounts with lazy_fallback == 0. A 100k-account state with a 10-tx block uses fewer than 2000 durable ops and never reads /pcc/state, within 1.5x of the 1k-account count. A broken touch set falls back to the identical root. Two MockHosts give identical dumps. The wasm still imports only smart_rollup_core.

Files. crates/chain-core/src/touch.rs (new), crates/chain-core/src/smt.rs, crates/kernel/src/lazy.rs (new), crates/kernel/src/storage.rs, crates/kernel/src/lib.rs, crates/kernel/tests/lazy_state.rs (new), crates/kernel/README.md

light-client: Light client from anchors (tiered trust) + data-availability budget (batch 5)

What people get. A phone or agent holding only the genesis params can check ‘my balance at H is X’ from a ~1 KB proof plus at most 100 headers. Each answer is labelled sequencer-signed, replayed by you, or L1-enforced. Mike gets a measured answer to what putting this chain’s data on Tezos costs.

Design. New no_std crate crates/light. LightClient::new(params, pinned genesis) ingests anchors at three tiers, halts on conflicting anchors with verifier Evidence::AnchorEquivocation, checks a header chain from the anchor to the target, and runs verify_account via smt::verify (legacy chains are deferred to chain-proof). Verifier::from_checkpoint starts VERIFY from an anchor. Explorer ‘light’ toggle with a tier badge, and a wasm rebuild. pc-sim —export-blocks plus kernel examples/da_budget.rs give docs/DATA_AVAILABILITY.md: inbox-per-block vs batched vs DAL. The empty-heartbeat finding (16,056 of 17,279 heights empty) scopes moving windows to L1 or timestamp time.

Acceptance. pointcast-light verifies 50 balances and 50 non-existence claims at tier SequencerSigned with ≤ 100 headers, proof ≤ 1.2 KB at 100k accounts. Forged anchors are ignored. Conflicting anchors give Evidence that evidence check accepts. A bad seal or link is refused. A MockHost outbox payload is ingested as L1Enforced. from_checkpoint at #200 reaches the genesis-replay root at #400. The wasm32 build succeeds. da_budget output is reproducible and recorded.

Files. crates/light/** (new), Cargo.toml (members), crates/verifier/src/replay.rs, crates/chain-wasm/src/ops.rs, crates/explorer/static/index.html, crates/explorer/static/verifier/*, crates/chain-sim/src/runner.rs, crates/chain-sim/src/main.rs, crates/kernel/examples/da_budget.rs (new), docs/DATA_AVAILABILITY.md (new)

forced-inclusion: Forced inclusion through the L1 inbox (based-sequencing fallback) (batch 6)

What people get. Anyone can push a signed tx straight into the Tezos rollup inbox. The sequencer must include it within D L1 levels, or the kernel builds the block itself on L1 time. Censorship becomes bounded delay, a vanished sequencer no longer stops the chain, and D = 0 runs PointCast as a fully based rollup.

Design. CONSENSUS behind LaunchParams record tag 4 forced{delay_levels, max_per_level, max_queue}. Block gains forced items applied first with skip semantics (forced_rejected receipts). header/v2 binds forced_root and forced_upto. The kernel parses StartOfLevel/InfoPerLevel and adds frame tag 4 ForcedTx with admission caps and a durable queue. Sequencer blocks must carry the due items byte-equal, otherwise the kernel builds an unsealed L1-origin block via chain_core::apply_kernel_block. The verifier recomputes L1-origin headers and gains forced_censorship evidence (kernel-context). node l1_follow.rs is a ForcedSource trait, MockHost only and unwired.

Acceptance. MockHost scenarios: honest inclusion; a censoring sequencer overridden at q+D with stale blocks refused; a silent sequencer for 500 levels; an invalid forced tx skipped; a 10k-frame flood capped with the honest item included within the bound; D=0 based mode equal to native apply_kernel_block. header/v2 goldens, legacy headers byte-identical, and mixed-chain verifier replay.

Files. crates/chain-core/src/forced.rs (new), crates/chain-core/src/types.rs, crates/chain-core/src/chain.rs, crates/chain-core/src/params.rs, crates/chain-core/src/state.rs, crates/kernel/src/inbox.rs, crates/kernel/src/forced.rs (new), crates/kernel/src/lib.rs, crates/kernel/src/codec.rs, crates/kernel/tests/forced_inclusion.rs (new), crates/verifier/src/replay.rs, crates/verifier/src/evidence.rs, crates/node/src/l1_follow.rs (new, unwired), DESIGN.md

attn-bridge: ATTN ticket bridge (built and tested, OFF by default) (batch 7)

What people get. Later, holders can move ATTN to Tezos L1 as tickets and back, with exits authorised by the rollup outbox and the refutation game rather than a custodian. Mandated agents can never withdraw an owner’s funds. It is not enabled on any value chain until FINDINGS-v2 gates prove farmed ATTN is negligible.

Design. CONSENSUS behind LaunchParams record tag 5 bridge{ticketer, min_withdraw, max_withdraw_per_epoch}, absent by default. Tx withdraw{amount, l1_receiver} is not delegable and moves balance to escrow, supply-neutral, with the invariant Σ balances + escrow == supply. Deposits arrive as a ForcedItem::Deposit. The kernel emits outbox %mint to the ticketer, accepts only (0,‘ATTN’) tickets from the configured ticketer, and records everything else in /pcc/bridge/stuck. contracts/attn_ticketer.tz is source only and never originated.

Acceptance. The escrow invariant proptest holds. Withdraw is rejected when disabled, below min, over the cap or malformed. An agent with all bits set gets OutOfScope. A MockHost withdraw gives exactly one decodable outbox %mint. A foreign ticket goes to stuck. A deposit over escrow is rejected. chain-sim keeps the escrow invariant for 30 days. Optional octez typecheck is skipped if absent.

Files. crates/chain-core/src/bridge.rs (new), crates/chain-core/src/types.rs, crates/chain-core/src/state.rs, crates/chain-core/src/forced.rs, crates/chain-core/src/params.rs, crates/kernel/src/bridge.rs (new), crates/kernel/src/codec.rs, crates/kernel/Cargo.toml, crates/kernel/tests/bridge.rs (new), crates/chain-sim/src/actors.rs, contracts/attn_ticketer.tz (new), contracts/README.md (new), DESIGN.md

Batch 1 ownership

Packages in a batch own disjoint paths, so they build in parallel worktrees and merge cleanly.

  • presence-tickets: crates/chain-core/src/params.rs, crates/chain-core/src/types.rs, crates/chain-core/src/state.rs, crates/chain-core/src/presence.rs, crates/chain-core/src/lib.rs, crates/chain-core/tests/presence.rs, crates/chain-core/tests/legacy_root.rs, crates/chain-core/tests/common/mod.rs, crates/kernel/src/codec.rs, crates/kernel/tests/codec_roundtrip.rs, crates/kernel/tests/mock_replay.rs, crates/kernel/tests/common/mod.rs, crates/verifier/src/testkit.rs, crates/verifier/tests/presence_replay.rs, crates/node/src/cli_drum.rs, crates/node/src/demo.rs, crates/node/src/main.rs (3 tiny hunks only: dev-genesis line ~152, dispatch line after attest-drum ~64, usage/ENV lines ~20 and ~37), issuer/**, crates/explorer/static/index.html, crates/explorer/static/verifier/**, DESIGN.md
  • town-hall: crates/town/**, scripts/town-demo.sh, Cargo.toml (one hunk: append "crates/town" to workspace members), README.md (new '## Town Hall' section only)
  • night-shift-ops: crates/node/src/ops.rs, crates/node/src/cli_doctor.rs, crates/node/src/cli_backup.rs, crates/node/tests/ops.rs, crates/node/tests/doctor.rs, crates/node/tests/backup_restore.rs, crates/node/src/lib.rs (3 added pub mod lines only), crates/node/src/main.rs (dispatch + usage hunks only), crates/node/src/api.rs (1 added merge line only), docs/RUNBOOK.md, ops/alerts.yml

The demo moment

On a laptop, three batch-1 branches merged onto a dev chain started with PC_TAP_POLICY=ticketed and run through scripts/town-demo.sh. At http://127.0.0.1:8550/town the sim-city room tiles glow where drumming just landed, and the FD, GDN and GF windows scroll in Win95 chrome. In the feed, Alice’s, Bob’s and Carol’s taps read ‘present · ticketed by ’ and fill their ATTN tanks, while the farm keys’ bare taps read ‘tap · no ticket · +0’. curl /metrics shows pointcast_accounts_at_epoch_cap staying flat. A tampered block from a stub node turns pc-town’s banner red with evidence instead of indexing it. Pressing VERIFY in the node explorer shows all green, replayed in the browser. Then pointcast-node doctor prints NOT READY and names why: dev sequencer key, treasury doubling as attestor admin. That is the honest bridge to batch 3, where scripts/rehearse-launch.sh ends in READY with the short list of steps only Mike can take.

Risks

  1. Presence moves the trust anchor to the issuer. Taps become only as sybil-resistant as PointCast logins. If identities are cheap (throwaway Google logins), farm income scales with identities instead of keys. Batch-2 economy-proof-v2 must measure this before any value genesis, and issuer-side policy (Kukai-only, account age, minimum hits) stays off-chain. The presence_reward, slot, cap and bind values in batch 1 are hypotheses. A compromised issuer is bounded by issuer_slot_cap × reward per slot, and the admin swaps the set.
  2. The consensus encoding has to be right the first time. The Params launch tail (0xF0 + sorted tagged records, reserved tags 1–5) and the generalized ext_root (modules 1 mandates, 2 presence, 3 sequencer, 4 capsules, 5 stations) are what every later consensus package builds on. The legacy_root golden, a pinned dev-genesis hash and the reproduced FINDINGS roots guard byte-identity for existing chains.
  3. Coordination with the Codex Proof-of-Balance lane. chain-proof decides state/v3 by !mandates.is_empty(), so after presence merges a presence-only state would give wrong proofs. The fix is a one-line switch to State::ext_root_opt(), done by the integrator after both merge. Recommended merge order: Codex PoB first, then presence-tickets, night-shift-ops, town-hall.
  4. Shared-file hunks. main.rs carries small hunks from Codex, presence and ops at deliberately separated base lines; Cargo.toml members is one line edited by both Codex and town (a trivial conflict); api.rs and lib.rs carry one or a few lines each. Wasm pins conflict whenever two lanes rebuild (batch 2: rotation plus drum-circle), so the integrator re-runs scripts/build-verifier.sh after the last merge.
  5. The kernel is its own workspace, so cargo test --workspace doesn’t cover it. Every consensus brief requires cd crates/kernel && cargo test, which depends on its crates being in the local cache (no network fetches).
  6. Machine load. Three parallel worktrees each build a full target dir on an iMac with a history of disk-full iCloud eviction and an earlier OOM. There is 85 GB free today; briefs stop below 15 GB.
  7. Re-genesis. Every presence, rotation, SMT, forced or bridge feature activates only on a new launch genesis: new genesis_hash, browser TOFU pins, re-registered agents and re-granted mandates. The cheap moment is before pointcast-1 carries value, which is now.
  8. Unverified live-wallet paths. Kukai and Temple signatures, including v2 amount-bearing text, are tested only against signer vectors. A real Kukai and a real Temple signature through the UI stay on Mike’s list, along with the cold sequencer_admin key, genesis acks, the funded anchor key and deploying the presence-issuer Worker.
  9. Tags above 15. Stations (16/17) need mandate::kind_bit to become a checked shift, or check_scope would overflow the u16 mask.
  10. The bridge (batch 7) must stay off until FINDINGS-v2 gates show farmed ATTN is negligible. Otherwise it turns farming into liquid L1 tokens.

Merge order

  1. Codex Proof of Balance (codex/proof-of-balance), after review.
  2. Then v2/presence-tickets, v2/night-shift-ops and v2/town-hall.
  3. After presence merges, switch chain-proof’s v3 rule from !mandates.is_empty() to State::ext_root_opt() (risk 3).
  4. Re-run the verifier build and pin after the last merge.