Skip to content

Integrating with the Wasteland — Dolt sync + DoltHub onboarding #1040

@jrf0110

Description

@jrf0110

Parent

Part of #204 (Phase 4: Hardening)

Revised 2026-03-31 (v3): Updated against canonical source at wasteland/schema/commons.sql and wasteland/internal/. Previous revisions were based on an outdated SKILL.md.

Problem

Gastown by Kilo towns cannot participate in the Wasteland federation. The Wasteland coordinates work across Gastown installations via Dolt databases on DoltHub. Our state lives in Durable Object SQLite — we need a bridge.

Non-Goals

  • Creating wastelands (we only join existing ones)
  • Gas City runtime adoption (we have our own reconciler)
  • Badge computation (read-only for now)
  • Acting as a Wasteland maintainer/validator (initially — we are a contributor)
  • Wild-west mode (DoltHub REST API does not support it — RemoteDB.CanWildWest() returns error at backend/remote.go:208-211)

Wasteland Protocol Summary

Grounded in wasteland/schema/commons.sql, wasteland/internal/commons/, and wasteland/internal/api/.

Core Model

The Wasteland is a federated work economy. Each participant ("rig") maintains a fork of a shared Dolt database (the "commons"). Work items ("wanted posters") are posted to the commons. Rigs browse, claim, complete, and get reputation stamps for their work. The fork-push-PR cycle is the only write path when using the DoltHub API backend.

Schema (v1.2 — wasteland/schema/commons.sql — 8 tables)

Table Purpose Our Integration
_meta Schema version (1.2) Read-only
rigs Rig registry — handle (PK), display_name, dolthub_org, hop_uri, owner_email, gt_version, trust_level, registered_at, last_seen, rig_type, parent_rig Write (register our town)
wanted Wanted board — id, title, description, project, type, priority, tags (JSON), posted_by, claimed_by, status, effort_level, evidence_url, sandbox_required, sandbox_scope (JSON), sandbox_min_tier, created_at, updated_at Read (consume) + Write (post)
completions Validated completions — id, wanted_id, completed_by, evidence, validated_by, stamp_id, parent_completion_id, block_hash, hop_uri, completed_at, validated_at Write (publish)
stamps Reputation stamps — id, author, subject, valence (JSON: {"quality": N, "reliability": N}), confidence (FLOAT), severity, context_id, context_type, skill_tags (JSON), message, prev_stamp_hash, block_hash, hop_uri, created_at. CHECK (NOT(author = subject)) — cannot self-stamp. Read (check reputation)
badges Achievement badges (first_blood, polyglot, bridge_builder) Read (display on dashboard)
boot_blocks Character sheet import data — handle (PK), source, sheet_json (JSON), confidence (FLOAT 0.0-1.0), version, created_at Read-only
chain_meta Hash chain metadata for tamper-evident federation Read
rig_links Cross-rig identity linking — rig_a, rig_b, link_type (same_owner), assertion_a/b, status (pending/confirmed/revoked), timestamps. CHECK (rig_a != rig_b), UNIQUE (rig_a, rig_b) Write (link agent rig to owner human rig)

Wanted Item Lifecycle

Source: wasteland/internal/commons/lifecycle.go:30-39

open --> claimed --> in_review --> completed
  |         |           |             ^
  |         v           v             +-- accept (+ reputation stamp)
  |      (unclaim)   (reject          +-- close  (no stamp)
  |         |       → claimed)
  |         v
  |       open
  |
  v
withdrawn

Key semantics:

  • Claiming is mandatory before submission. SubmitCompletion (commons.go:371) requires WHERE status='claimed' AND claimed_by=?. No skip-claim/bounty path.
  • Reject sends in_review → claimed (NOT back to open). The claimer retains the claim and can resubmit. The completion record is deleted during rejection (commons.go:887-891).
  • Accept issues a reputation stamp and sets status='completed'.
  • Authorization is role-based (lifecycle.go:146-170): poster, claimer, admin — NOT trust-level-based.

Wanted Type Values

feature, bug, design, rfc, docs, inference — map to bead labels. The inference type is for verifiable distributed LLM inference jobs (docs/verifiable-inference.md).

Sandbox Fields

The wanted table includes sandbox_required (TINYINT), sandbox_scope (JSON), and sandbox_min_tier (VARCHAR) for items requiring sandboxed execution. When importing wanted items as beads, check sandbox_required and note it in bead metadata for container environment configuration.

Trust Levels

Stored in rigs.trust_level (INT DEFAULT 0). Registration hard-codes trust_level = 1 (registration.go:10). Trust levels are NOT enforced by the codebase — authorization in lifecycle.go uses role-based logic (poster, claimer, admin list), not trust levels. Trust levels are a social signal, not a technical gate.

Authentication

DoltHub API tokens (NOT OAuth). Source: backend/remote.go:454:

  • User creates token at dolthub.com/settings/tokens
  • Header: Authorization: token $DOLTHUB_TOKEN
  • Base URL: https://www.dolthub.com/api/v1alpha1
  • Hosted mode uses Nango for OAuth delegation (hosted/nango.go), but underlying auth is still DoltHub tokens.

Rig Registration

registration.go:8-12 generates:

hopURI := fmt.Sprintf("hop://%s/%s/", ownerEmail, handle)

Registers with trust_level = 1. We need the owner's email to construct the HOP URI.

Write Model: Fork-Push-PR (mandatory)

PR mode is the only option for API-based integration (RemoteDB.CanWildWest() returns error). Branch naming: wl/<rig-handle>/<wanted-id>.

  1. Fork the upstream commons to your DoltHub org
  2. Commit changes to a branch on your fork
  3. Push to DoltHub
  4. Upstream maintainer merges via DoltHub PR

REST API Surface

Source: wasteland/internal/api/routes.go:4-52 and hosted/server.go:53-59

Read endpoints (callable from TownDO via DoltHub REST API)

Endpoint Purpose
GET /api/wanted Browse board (query params for filtering)
GET /api/wanted/{id} Item detail
GET /api/dashboard Personal dashboard
GET /api/scoreboard/detail Leaderboard detail
GET /api/profile/{handle} Rig profile

Mutation endpoints (require fork-push, done via wl CLI in container)

Endpoint CLI Command Purpose
POST /api/wanted wl post Post new wanted item
POST /api/wanted/{id}/claim wl claim <id> Claim item
POST /api/wanted/{id}/unclaim wl unclaim <id> Release claim
POST /api/wanted/{id}/done wl done <id> --evidence <url> Submit completion
POST /api/sync wl sync Force sync

Important: We don't call the Wasteland REST API directly for writes. The mutation endpoints are for the Wasteland's own web UI. Our integration uses the wl CLI binary in the container, which handles the fork-push-PR cycle internally.


Architecture

TownDO (SQLite — source of truth)
  ↕ reconciler events
  ↓
Alarm loop:
  ├── Inbound: poll wanted board via DoltHub REST API
  │   (same API as RemoteDB: GET /api/v1alpha1/{org}/{db}/main?q=...)
  │   Requires: DOLTHUB_TOKEN header
  └── Outbound: on bead close → emit wasteland_completion_ready event
  ↓
Reconciler side effects:
  ├── publish_wasteland_completion → call container to run `wl done`
  ├── claim_wasteland_item → call container to run `wl claim`
  └── sync_wasteland_board → call container to run `wl sync`
  ↓
Container:
  ├── `wl` CLI binary (v0.3.0, ~50MB, installed in Dockerfile)
  ├── Pre-configured: `wl join` during onboarding
  ├── Env: DOLTHUB_TOKEN, DOLTHUB_ORG
  └── Agent processes (unchanged — polecats work on beads, not Wasteland)

Reconciler Integration

New Event Types

'wasteland_items_synced'        // Poll completed, new items found
'wasteland_completion_ready'    // Bead closed with wasteland origin
'wasteland_published'           // Completion pushed to fork successfully
'wasteland_publish_failed'      // Completion push failed (for retry)
'wasteland_claimed'             // Item claimed on wasteland

New Reconciler Rules (reconcileWasteland)

Rule 1: Publish completions for closed beads with wasteland origin
  WHERE status = 'closed'
    AND metadata.wasteland_wanted_id IS NOT NULL
    AND metadata.wasteland_published_at IS NULL
  → emit publish_wasteland_completion

Rule 2: Escalate stale completions (published > 7 days, not validated)
  → create escalation for Mayor

Rule 3: Retry failed publishes (with backoff, max 3 attempts)
  → re-emit publish_wasteland_completion

New Action Types

PublishWastelandCompletion: { bead_id, wasteland_wanted_id, connection_id, evidence_url }
ClaimWastelandItem: { wasteland_wanted_id, connection_id }
SyncWastelandBoard: { connection_id }

All are side effects (deferred async container calls), similar to dispatch_agent and poll_pr.

Alarm Loop Pre-Phase

if (townConfig.wasteland?.enabled) {
  for (const conn of townConfig.wasteland.connections) {
    const lastSync = getLastWastelandSync(this.sql, conn.id);
    if (Date.now() - lastSync > conn.poll_interval_ms) {
      const items = await this.pollWastelandBoard(conn);
      events.insertEvent(this.sql, 'wasteland_items_synced', {
        payload: { items, connection_id: conn.id },
      });
    }
  }
}

TownConfig Schema

wasteland?: z.object({
  enabled: z.boolean().default(false),
  connections: z.array(z.object({
    id: z.string().uuid(),
    upstream: z.string(),              // e.g. "hop/wl-commons"
    fork: z.string(),                  // e.g. "our-org/wl-commons"
    dolthub_token: z.string(),         // encrypted
    rig_handle: z.string(),
    owner_email: z.string(),           // needed for HOP URI construction
    provider: z.enum(['dolthub', 'github']).default('dolthub'),
    poll_interval_ms: z.number().default(300_000),
  })).default([]),
  otel_enabled: z.boolean().default(false),
})

Multi-wasteland support per SKILL.md.


Mayor Tools

Tool wl Equivalent Description
gt_wasteland_browse wl browse Browse available wanted items, filter by project/tags/priority/type
gt_wasteland_post wl post Post a new wanted item to the board
gt_wasteland_claim wl claim Claim a wanted item → creates a bead in the town
gt_wasteland_publish wl done Publish completion with evidence (auto-triggered on bead close, or manual)
gt_wasteland_status wl me + wl status Town's Wasteland profile, reputation, badges, pending items
gt_wasteland_sync wl sync Force an immediate sync

Polecats have NO Wasteland tools. The Wasteland boundary is at the reconciler + Mayor level. Imported wanted items become regular beads; closures trigger automatic completion publishing.


Onboarding Flow

  1. User navigates to town settings → "Wasteland" section
  2. Clicks "Connect to Wasteland"
  3. User provides:
    • DoltHub API token (from dolthub.com/settings/tokens)
    • Upstream commons URL (default: hop/wl-commons)
    • Rig handle (auto-suggested)
    • Owner email (for HOP URI: hop://{email}/{handle}/)
  4. We validate the token against the DoltHub API
  5. We fork the upstream commons to the user's DoltHub org
  6. We run wl join <upstream> in the container
  7. We register the town as a rig: rig_type = 'agent', parent_rig = <human-owner-handle>, trust_level = 1
  8. Create a rig_links entry linking the agent rig to the human owner's rig
  9. Incremental sync begins (alarm-driven polling + event-driven publishing)

Container Changes

Tier 1 (required)

# Install Wasteland CLI
RUN curl -fsSL https://github.com/gastownhall/wasteland/releases/download/v0.3.0/wasteland_0.3.0_linux_amd64.tar.gz | tar xz && \
    mv wl /usr/local/bin/wl

Container env vars: DOLTHUB_TOKEN, DOLTHUB_ORG

Tier 2 (future)

Install Dolt binary (~100MB) + local MySQL server for sub-ms queries, versioned history, offline resilience.


Acceptance Criteria

Tier 1

  • Wasteland config section in town settings (token, upstream, rig handle, email)
  • DoltHub token validation at onboarding
  • Fork creation on DoltHub
  • Rig registration (rig_type='agent', parent_rig, trust_level=1, HOP URI)
  • rig_links entry for agent-to-human linking
  • Inbound sync: wanted board polled, new items imported as beads
  • Outbound sync: bead completions published via wl done in container
  • All completions preceded by claim (enforced by Wasteland: claimed status required before done)
  • Mayor tools: browse, post, claim, publish, status, sync
  • Polecats have NO Wasteland tools
  • Multi-wasteland support in TownConfig
  • wl binary v0.3.0 installed in container Dockerfile
  • Wanted type mapped to bead labels (feature, bug, design, rfc, docs, inference)
  • Sandbox fields (sandbox_required, sandbox_scope, sandbox_min_tier) mapped to bead metadata
  • Wasteland health check in diagnostics
  • Validation tracking (published_at, validated_at) on bead metadata
  • Stale completion escalation (published > 7d, not validated)
  • Schema v1.2 compatibility (8 tables)
  • New reconciler events, rules, and actions per architecture section
  • PR mode enforced (no wild-west)
  • Reject semantics: in_review → claimed (claimer retains claim)

Tier 2 (future)

  • Dolt binary in container
  • Local Dolt MySQL server
  • Offline operation with sync-on-reconnect
  • Merge conflict resolution

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Backlog / futureenhancementNew feature or requestgt:coreReconciler, state machine, bead lifecycle, convoy flowkilo-auto-fixAuto-generated label by Kilokilo-triagedAuto-generated label by Kilo

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions