Skip to content

Latest commit

 

History

History
414 lines (306 loc) · 12.5 KB

File metadata and controls

414 lines (306 loc) · 12.5 KB

Property Layers: Multi-Level Configuration

Implementation guide for Gas Town's configuration system. Created: 2025-01-06

Overview

Gas Town uses a layered property system for configuration. Properties are looked up through multiple layers, with earlier layers overriding later ones. This enables both local control and global coordination.

The Four Layers

┌─────────────────────────────────────────────────────────────┐
│ 1. WISP LAYER (transient, town-local)                       │
│    Location: <rig>/.beads-wisp/config/                      │
│    Synced: Never                                            │
│    Use: Temporary local overrides                           │
└─────────────────────────────┬───────────────────────────────┘
                              │ if missing
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 2. RIG BEAD LAYER (persistent, synced globally)             │
│    Location: <rig>/.beads/ (rig identity bead labels)       │
│    Synced: Via git (all clones see it)                      │
│    Use: Project-wide operational state                      │
└─────────────────────────────┬───────────────────────────────┘
                              │ if missing
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 3. TOWN DEFAULTS                                            │
│    Location: ~/gt/config.json or ~/gt/.beads/               │
│    Synced: N/A (per-town)                                   │
│    Use: Town-wide policies                                  │
└─────────────────────────────┬───────────────────────────────┘
                              │ if missing
                              ▼
┌─────────────────────────────────────────────────────────────┐
│ 4. SYSTEM DEFAULTS (compiled in)                            │
│    Use: Fallback when nothing else specified                │
└─────────────────────────────────────────────────────────────┘

Lookup Behavior

Override Semantics (Default)

For most properties, the first non-nil value wins:

func GetConfig(key string) interface{} {
    if val := wisp.Get(key); val != nil {
        if val == Blocked { return nil }
        return val
    }
    if val := rigBead.GetLabel(key); val != nil {
        return val
    }
    if val := townDefaults.Get(key); val != nil {
        return val
    }
    return systemDefaults[key]
}

Stacking Semantics (Integers)

For integer properties, values from wisp and bead layers add to the base:

func GetIntConfig(key string) int {
    base := getBaseDefault(key)    // Town or system default
    beadAdj := rigBead.GetInt(key) // 0 if missing
    wispAdj := wisp.GetInt(key)    // 0 if missing
    return base + beadAdj + wispAdj
}

This enables temporary adjustments without changing the base value.

Blocking Inheritance

You can explicitly block a property from being inherited:

gt rig config set gastown auto_restart --block

This creates a "blocked" marker in the wisp layer. Even if the rig bead or defaults say auto_restart: true, the lookup returns nil.

Rig Identity Beads

Each rig has an identity bead for operational state:

id: gt-rig-gastown
type: rig
name: gastown
repo: git@github.com:steveyegge/gastown.git
prefix: gt

labels:
  - status:operational
  - priority:normal

These beads sync via git, so all clones of the rig see the same state.

Two-Level Rig Control

Level 1: Park (Local, Ephemeral)

gt rig park gastown      # Stop services, daemon won't restart
gt rig unpark gastown    # Allow services to run
  • Stored in wisp layer (.beads-wisp/config/)
  • Only affects this town
  • Disappears on cleanup
  • Use: Local maintenance, debugging

Level 2: Dock (Global, Persistent)

gt rig dock gastown      # Set status:docked label on rig bead
gt rig undock gastown    # Remove label
  • Stored on rig identity bead
  • Syncs to all clones via git
  • Permanent until explicitly changed
  • Use: Project-wide maintenance, coordinated downtime

Daemon Behavior

The daemon checks both levels before auto-restarting:

func shouldAutoRestart(rig *Rig) bool {
    status := rig.GetConfig("status")
    if status == "parked" || status == "docked" {
        return false
    }
    return true
}

Configuration Keys

Key Type Behavior Description
status string Override operational/parked/docked
auto_restart bool Override Daemon auto-restart behavior
max_polecats int Override Maximum concurrent polecats
priority_adjustment int Stack Scheduling priority modifier
maintenance_window string Override When maintenance allowed
dnd bool Override Do not disturb mode

Commands

View Configuration

gt rig config show gastown           # Show effective config (all layers)
gt rig config show gastown --layer   # Show which layer each value comes from

Set Configuration

# Set in wisp layer (local, ephemeral)
gt rig config set gastown key value

# Set in bead layer (global, permanent)
gt rig config set gastown key value --global

# Block inheritance
gt rig config set gastown key --block

# Clear from wisp layer
gt rig config unset gastown key

Rig Lifecycle

gt rig park gastown          # Local: stop + prevent restart
gt rig unpark gastown        # Local: allow restart

gt rig dock gastown          # Global: mark as offline
gt rig undock gastown        # Global: mark as operational

gt rig status gastown        # Show current state

Examples

Temporary Priority Boost

# Base priority: 0 (from defaults)
# Give this rig temporary priority boost for urgent work

gt rig config set gastown priority_adjustment 10

# Effective priority: 0 + 10 = 10
# When done, clear it:

gt rig config unset gastown priority_adjustment

Local Maintenance

# I'm upgrading the local clone, don't restart services
gt rig park gastown

# ... do maintenance ...

gt rig unpark gastown

Project-Wide Maintenance

# Major refactor in progress, all clones should pause
gt rig dock gastown

# Syncs via git - other towns see the rig as docked
bd sync

# When done:
gt rig undock gastown
bd sync

Block Auto-Restart Locally

# Rig bead says auto_restart: true
# But I'm debugging and don't want that here

gt rig config set gastown auto_restart --block

# Now auto_restart returns nil for this town only

Implementation Notes

Wisp Storage

Wisp config stored in .beads-wisp/config/<rig>.json:

{
  "rig": "gastown",
  "values": {
    "status": "parked",
    "priority_adjustment": 10
  },
  "blocked": ["auto_restart"]
}

Rig Bead Labels

Rig operational state stored as labels on the rig identity bead:

bd label add gt-rig-gastown status:docked
bd label remove gt-rig-gastown status:docked

Daemon Integration

The daemon's lifecycle manager checks config before starting services:

func (d *Daemon) maybeStartRigServices(rig string) {
    r := d.getRig(rig)

    status := r.GetConfig("status")
    if status == "parked" || status == "docked" {
        log.Info("Rig %s is offline, skipping auto-start", rig)
        return
    }

    d.ensureWitness(rig)
    d.ensureRefinery(rig)
}

Operational State Events

Operational state changes are tracked as event beads, providing an immutable audit trail. Labels cache the current state for fast queries.

Event Types

Event Type Description Payload
patrol.muted Patrol cycle disabled {reason, until?}
patrol.unmuted Patrol cycle re-enabled {reason?}
agent.started Agent session began {session_id?}
agent.stopped Agent session ended {reason, outcome?}
mode.degraded System entered degraded mode {reason}
mode.normal System returned to normal {}

Creating and Querying Events

# Create operational event
bd create --type=event --event-type=patrol.muted \
  --actor=human:overseer --target=agent:deacon \
  --payload='{"reason":"fixing convoy deadlock","until":"gt-abc1"}'

# Query recent events for an agent
bd list --type=event --target=agent:deacon --limit=10

# Query current state via labels
bd list --type=role --label=patrol:muted

Labels-as-State Pattern

Events capture the full history. Labels cache the current state:

  • patrol:muted / patrol:active
  • mode:degraded / mode:normal
  • status:idle / status:working

State change flow: create event bead (immutable), then update role bead labels (cache).

# Mute patrol
bd create --type=event --event-type=patrol.muted ...
bd update role-deacon --add-label=patrol:muted --remove-label=patrol:active

Configuration vs State

Type Storage Example
Static config TOML files Daemon tick interval
Role directives Markdown files Operator behavioral policy per role
Formula overlays TOML files Per-step formula modifications
Operational state Beads (events + labels) Patrol muted
Runtime flags Marker files .deacon-disabled

Events are the source of truth. Labels are the cache.

For Boot triage and degraded mode details, see Watchdog Chain.

Role Directives and Formula Overlays

Directives and overlays extend the property layer model to agent behavior. They follow the same rig > town > system precedence as other config.

Directives (Behavioral Policy)

Per-role Markdown files that modify agent behavior at prime time:

SYSTEM LAYER:   Embedded role template (compiled in)
                        │ if directive exists
                        ▼
TOWN LAYER:     ~/gt/directives/<role>.md
                        │ concatenated with
                        ▼
RIG LAYER:      ~/gt/<rig>/directives/<role>.md

Both town and rig directives concatenate. Rig content appears last and wins conflicts (same as CSS specificity — later rules override earlier ones).

Overlays (Formula Modifications)

Per-formula TOML files that modify individual steps:

SYSTEM LAYER:   Embedded formula (compiled in)
                        │ if overlay exists
                        ▼
TOWN LAYER:     ~/gt/formula-overlays/<formula>.toml
                        │ rig replaces town entirely
                        ▼
RIG LAYER:      ~/gt/<rig>/formula-overlays/<formula>.toml

Unlike directives, overlays use full replacement at the rig level — if a rig overlay exists, the town overlay is ignored entirely. This prevents conflicting step modifications from merging unpredictably.

Precedence Summary

Config Type Town + Rig Interaction Rationale
Rig properties First non-nil wins (override) Standard config lookup
Integer properties Values stack (additive) Allows adjustments
Role directives Concatenate (rig last) Additive policy; rig gets last word
Formula overlays Rig replaces town Step mods can conflict; full replacement is safer

See directives-and-overlays.md for the full reference with TOML format, examples, and gt doctor integration.

Related Documents

  • ~/gt/docs/hop/PROPERTY-LAYERS.md - Strategic architecture
  • wisp-architecture.md - Wisp system design
  • agent-as-bead.md - Agent identity beads (similar pattern)
  • directives-and-overlays.md - Full reference