chrisscho.uk

The lab

How the systems are actually put together

The engineering detail, for anyone who wants it. How the agent setup got here, architecture, the pipelines I run on my own music releases, and the scripts that keep my week running. If you're here to work out where your business is losing time, the AI Workflow Audit is the better door.

There is also a running list of engineering learnings, one line each, with the file or the change you can check each one against.

The road here

Eight phases, including the two that went nowhere

How the setup got from a prototype that found nothing to the agents running now. The dead ends are on here because they are where the lessons are.

  1. Dec 2024PrototypeTurning point

    A Perplexity prototype that found nothing

    The first attempt at automated contact research returned a 0% success rate. It asked broad questions and got broad answers back. Narrowing the question turned out to be the whole job.

    Perplexity
  2. Jan–Aug 2025First productMilestone

    Audio Intel ships as a standalone tool

    Contact enrichment became a product in its own right rather than a script. Eight months of building against real campaigns.

  3. Sep–Dec 2025SprawlDead end

    Three apps across three subdomains

    Splitting the work across separate apps and subdomains tripled the deployment surface and the maintenance without tripling anything useful. Consolidated back into one platform in Jan 2026.

  4. Jan–Feb 2026First persistent agentMilestone

    OpenClaw goes live, later named Dougal

    The first agent that stayed up between sessions rather than starting cold each time, bridged to a phone. Persistence changed what it was reasonable to ask for.

    OpenClaw
    Read the write-up →
  5. Mar 2026The board of directorsTurning point

    Paperclip runs eight agents, and Ted arrives

    Eight named agents, heartbeats, a shared task queue, and a Monday briefing, running alongside Ted, a persistent Claude Code session bridged to a phone. The question it raised: does a solo founder need agents that coordinate, or skills that do one thing well.

    Claude CodeOpenClaw
    Read the write-up →
  6. Apr–May 2026The correctionTurning point

    Paperclip paused, skills cemented

    Tasks went stale, and no agent stopped to ask a clarifying question. Paperclip was mass-disabled on 7 May, and the setup rebuilt around a skill library, with the project memory rewritten to match a week later.

    Read the write-up →
  7. Jun–Aug 2026HardeningMilestone

    Hermes moves off the Mac, onto a VPS

    The ambient ops agent started life on the Mac and moved to a small always-on server, hardened from a Discord novelty into an actual co-founder with scheduled jobs of its own. On 16 Aug the Mac side of the gateway was retired for good, after it started double-connecting the same Discord bot the VPS was already running.

    Hermes
  8. Aug 2026NowCurrent

    Pi joins as the daily driver, visual rules land

    Pi, an interactive coding agent distinct from Claude Code's automations and Hermes's ambient ops, now runs on the same VPS as the daily driver. Visual work gets a mandatory design-review pass, so the agents get judged on what they render rather than only on what they log.

    Pi
[ CAMPAIGN FLOWS // VOL. 02 ]

Dogfooding on sadact

How Chris coordinates his custom audio tools, masters tracks against golden profiles, validates outreach contacts, and stages outreach campaigns.

01

Composition & Pattern Sequence

Chris composes and sequences track structures locally using custom command patterns in audx, bypassing complex graphical DAWs.

[audx_daemon]audx render-project project.audx --stems --output ./renders/
10:15:02
02

Float64 DSP & Profile Matching

Rendered stems pass into the finisher engine. The track is dynamically compressed and spectrally matched against custom UK Garage master charts.

[sadact_finisher]finisher process ./renders/stem_mix.wav --profile ukg --loudness streaming
10:15:30
03

Contact Hygiene & Typo Correction

The campaign mailing sheet runs through Datasink to remove invalid MX domains, correct common typos, and collapse Jaro-Winkler duplicate names.

[datasink_scrub]sink wash ./contacts/campaign_list.csv --provider anthropic
10:16:05
04

Personalized Pitch Generation

TAP's pitching module scans contact relationship warmth, notes target formats, and drafts three distinct email copy variants.

[tap_pitcher]tap-mcp generate-pitch --artist sadact --contacts ./contacts_clean.csv
10:16:15
05

Staged Drafts Review & Release

Generated drafts are pushed into a labeled Gmail inbox. Chris reviews, tweaks the phrasing, and authorizes the sends.

[gmail_mcp]gmail-mcp stage-drafts --label 'TAP: sadact Outreach' --verify-preflight
10:16:22
PIPELINE RUN REPORT
// Observer Environment
VARVALUE
Pipelinesadact Release & Mastering Loop
Steps5 loaded
Contextsadact Release
// Console summary

Release pipeline completed. 1 master rendered (Float64 UKG profile), 42 contacts scrubbed, 42 pitch drafts staged in Gmail queue. System state: STAGED_APPROVALS_AWAITING_USER.

Observer check: SUCCESS (code 200)
[ ARCHITECTURE // VOL. 04 ]

Agentic Infrastructures & Knowledge Bases

I build contextual agent systems that generalize to any business pipeline. By mapping internal knowledge vaults directly to custom coding environments and LLMs, we create self-contained operating loops.

Obsidian Knowledge Base Context Grounding Loop

[ 01 // OBSIDIAN VAULT ]Markdown Notes & Wikisgit-sync (SSH publickey)GIT PULL[ 02 // VECTOR RAG ]Heuristics & Prompts Extractorclaude-context-generatorFEED LLM[ 03 // CLAUDE / WORKSPACE ]Execution Engine (Claude Code / MCP)Outcome: Staged SaaS Campaigns / Builds

Obsidian 'Knowledge Base' Grounding

Operational Core

My personal knowledge graph is compiled inside an Obsidian vault. It is git-synchronized and automatically indexed, allowing Claude to read structured markdown logs, design heuristics, and format rules to ground prompts in deep context.

Specs:
sync_Plugin:Obsidian Git (Auto-Push/Pull)
context_Format:Markdown Vault wikilinks
grounding_Role:LLM contextual injection (RAG / Vault nodes)
{
  "obsidian_sync": {
    "vault_path": "~/documents/personal_wiki",
    "sync_interval_mins": 15,
    "ssh_key": "id_ed25519_obsidian"
  }
}

Claude Code & MCP Integrations

Active Environment

My local command-line terminal operations. Connected to Model Context Protocol (MCP) servers that allow Claude to read local databases, coordinate project files, and write code scripts for any client vertical.

Specs:
cli_Environment:Claude Code / Claude CLI
connected_Tools:SQLite DB, local file editor, script runner
gmail_Interface:mcp-gmail-stage (staged compose queues)
{
  "mcp_config": {
    "servers": {
      "filesystem": {
        "allowed_paths": [
          "./src",
          "./docs"
        ]
      },
      "gmail-stage": {
        "port": 3009,
        "staged_label": "Drafts"
      }
    }
  }
}

Generalizable Automation Pipelines

Flexible Architecture

These systems are built to fit any operational domain (SaaS, media, e-commerce). By combining cron checks, LLM relevance filters, and structured schema validations, we turn arbitrary web feeds or APIs into structured database entries.

Specs:
cron_Daemon:24/7 VPS check loop (Node/Python)
llm_Classifier:Claude-3-Haiku relevance threshold checks
notifications:Slack webhook status alerts & Notion triggers
{
  "general_pipeline": {
    "input_type": "any_rss_or_api",
    "relevance_cutoff": 85,
    "max_monthly_tokens": 2000000
  }
}
[ OPEN SOURCE SHELF // VOL. 05 ]

Solopreneur Automation Kit

Production scripts I write to orchestrate my daily backups, guard my coding stamina, and sync environment states. Copy, customize, and run them locally.

session-time-guard.jsjavascript

A terminal monitor that tracks development session length on your active Git branch, issues break alerts, and logs focus metrics directly to a local CSV file.

#!/usr/bin/env node
/**
 * Session Time Guard - Dev Stamina Monitor
 * Tracks active development sessions and logs focus milestones.
 */
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';

const LOG_FILE = path.join(process.env.HOME, '.solopreneur', 'focus-sessions.csv');
const MAX_DURATION_MINUTES = 120;
const WARNING_MINUTES = 90;

function getCurrentBranch() {
  try {
    return execSync('git rev-parse --abbrev-ref HEAD', {
      encoding: 'utf-8'
    }).trim();
  } catch {
    return 'detached';
  }
}

export function startSession(task) {
  const session = {
    startTime: new Date().toISOString(),
    task: task || 'General Refactoring',
    branch: getCurrentBranch()
  };
  fs.writeFileSync(
    path.join(process.env.HOME, '.solopreneur', 'current-session.json'),
    JSON.stringify(session, null, 2)
  );
  console.log(`[GUARD] Started focus session on branch: ${session.branch}`);
}

export function checkStamina(session) {
  const elapsed = (Date.now() - new Date(session.startTime).getTime()) / 60000;
  if (elapsed >= MAX_DURATION_MINUTES) {
    console.log(`[ALERT] Session exceeded ${MAX_DURATION_MINUTES}m! Step away from the monitor.`);
  } else if (elapsed >= WARNING_MINUTES) {
    console.log(`[WARNING] Stamina threshold met (${WARNING_MINUTES}m). Consider taking a 10m break.`);
  }
}
Copy file contents directly to use locallyReady to use

If you came here from the business side

The same method, pointed at your week instead of mine.

Everything above is the setup I run on my own company. The audit applies it to yours and finds the hours going spare.