“the surface tightens — kanban, opt-in, MCP, update flow.”

Distribution work doesn’t look like building

Day 17 was a build day: orchestration, security cycle, pipeline UI. Day 18 wasn’t another build day — it was a tightening day. Gating power features behind opt-in flags. Defaulting to clean surfaces. Surfacing the operator-facing strings that were leaking the operator’s machine layout. Naming things on registries before squatters do.

None of it added new capability. All of it reduced friction for the next operator who isn’t me.

Pending → kanban board

The single-list /dashboard/pending page became a 4-column kanban: Waiting / Approved / Denied / Expired. Same data source, same approve/deny actions, same 10s polling — only the rendering changed.

Per-column accent strip (gold / green / red / grey) gives quick visual differentiation. Empty columns render a muted explanatory line instead of collapsing. Stacks to one column on ≤900px. Card visuals carry over identically.

The operator’s mental model didn’t change; the time to triage did. Round-tripping through a status-filter dropdown was the old friction.

Design Studio → opt-in (--studio)

The orchestration plane is nontrivial. Four specialist agents, a project canvas, a tools allowlist, a pipeline indicator. That’s the right shape for the operator who’s using it; it’s NOT the right shape for someone who just typed werbos serve to try the chat REPL.

Flipped studio to default OFF, opt-in via werbos serve --studio. Four surfaces gated by SessionConfig.studio_enabled:

  1. /api/studio/* routes — not mounted when off
  2. --tools image_generate (or screenshot_url) without --studio raises SystemExit with a helpful pointer
  3. Dashboard /studio nav link — hidden via the new features.studio field on /api/settings
  4. Settings API surfaces the flag for the dashboard

FEATURE_GATED_ROUTES map makes the next opt-in route a one-line addition.

For SR8 (which DOES want the studio surface), added --studio to the compose command. New pip-install users get the clean default; SR8 keeps what it had.

NX.1 preflight + npm name reservation

Drafted the npm-wrapper sprint as a ~50-line JS shim that forwards every call through uvx werbos .... First-run helper detects + prompts for uv install. Python wheel stays canonical; JS is the delivery vehicle for JS-flavored developers who don’t have Python set up.

Preflight confirmed:

  • werbos is FREE on both npm AND PyPI today. Squat risk if we sit.
  • Node 22.15.1 + npm 11.10.1 on operator’s box.
  • uv not installed locally → first-run UX is “print install command, exit non-zero.” No auto-curl.
  • No werbos versions on PyPI → NX.1 takes ownership of the publish.

Six locked decisions queued; five-day shape (skeleton → ensureUv → version pin → CI → README + publish). macOS + Linux v1, Windows v1.1.

Reserved the npm name with a metadata-only 0.0.0 stub at werbos/npm/. package.json + README, no bin, no install hook. npm pack --dry-run: 1.1 KB tarball, 2 files. Operator runs npm publish to claim — blocked tonight by passkey-on- other-device + non-working recovery code. Will sort with an Automation token tomorrow.

IDEAS.md + GO-LIVE.md as durable scratch

Two artifacts at repo root:

  • GO-LIVE.md (operator-authored) — 286-line v1 punch list. Three columns per feature: Ship / Help / You tested. Sources: README, ROADMAP, gap-hunting, MEMORY.md.
  • IDEAS.md — 12 feature ideas brainstormed against the post-v1-survey state. Each tied to an existing primitive (not vaporware), each scoped (small / medium / larger). Cross-references existing sprint docs to stay tight on what’s NEW.

Two complementary lenses: GO-LIVE.md is “what exists that needs verification”; IDEAS.md is “what doesn’t exist yet that might be worth building.” Both scratch. Sprint docs in docs/sprints/ are the commitment layer.

MCP.1 — personal-bot MCP layer

The operator wants a BTC trading bot. A Minecraft co-builder bot. Whatever future personal bots. None of those should ship to PyPI / npm / the public werbos surface — they’re private.

The right shape: each bot is a small standalone MCP server (operator’s own code, operator’s own private repo), and werbos becomes a great MCP client that discovers + registers their tools + gates every invocation through the existing perms + PendingAction kanban.

Sprint MCP.1 drafted. Two concrete use cases walked end-to-end: BTC trader (real money, perm=ask, every trade lands on the kanban) + Minecraft co-builder (in-game actions, perm=allow). Six locked decisions (TOML format, stdio v1 transport, dot- namespaced tool names, default-deny perms, auto-respawn on crash, no inline tool docs).

/dashboard/mcp page + /api/mcp/info

Shipped the UI for both MCP directions, even though MCP.1’s inbound runtime is still drafted-not-built:

  • Outbound (“Connect werbos to your AI tools”) — copy- paste configs for Claude Desktop + Claude Code, list of the 7 tools werbos’s existing werbos mcp serve exposes. Functional today.
  • Inbound (“Connect your bots to werbos”) — reads ~/werbos/mcp.toml, lists declared servers. Today: read- only metadata. Tomorrow (MCP.1): live tool registration.

Always-on (unlike /studio which is opt-in). The outbound surface is the existing werbos mcp serve; the inbound config layer is operator-visible documentation that grows into a live runtime when MCP.1 lands.

Path-leak fix — ~ for home, all-platforms in docs

Operator-facing surfaces were leaking two things over HTTP:

  1. OS family — /Users/<x> says macOS, /home/<x> says Linux, C:\Users\<x> says Windows.
  2. Operator username — /Users/jasonsutter/....

New helper werbos/server/_paths.py::tilde_path() collapses home-relative paths to ~/<rest>. Paths outside home (e.g. /data/... on the SR8 container) pass through unchanged.

Applied to /api/settings.{data_dir,agents_dir,chats_dir}, /api/mcp/info.inbound.config_path, /api/updates/check. upgrade_command. Dashboard MCP page now shows ALL THREE Claude Desktop config paths (macOS / Windows / Linux) instead of “macOS or the equivalent.”

7 new tests pin the edge cases.

Settings → Updates section

“Pull latest + reboot” affordance in Settings. Detects how werbos was installed (pip / git / docker / unknown), shows current version + latest from PyPI, renders the exact upgrade command for that install method with a copy-to-clipboard button.

v1 scope: read-only. The endpoint surfaces info; the dashboard shows the command; the OPERATOR runs the command in their terminal. No subprocess spawns, no auto-restart, no kanban gate. v2 can layer one-click upgrade once the UX is right.

Install-method detection is stdlib-only + cross-platform. Container detection via /.dockerenv + /proc/1/cgroup heuristics. Git-editable via walking up looking for .git. PyPI fallback via importlib.metadata. PyPI version probe with a 5s timeout, soft-fails to “not yet published” today since werbos isn’t on PyPI.

The dashboard-lives-in-nginx bug

Shipped the Updates section. Tried to hard-refresh SR8 to verify the new dashboard. Saw the OLD nav (no Studio, no Pending kanban, no MCP). Hard-refreshed again. Still old.

The dashboard SPA isn’t served by the Python werbos container. It’s a static nginx-served bundle baked into the suite-ui image’s /usr/share/nginx/html/dashboard/. Every time I’d been running docker compose build werbos && docker compose up -d werbos this session, the NGINX image stayed at its first-deploy state from weeks ago. The new Python kept landing; the dashboard never moved.

Fix: drop the service filter on the upgrade command. Full docker compose build && docker compose up -d rebuilds + restarts EVERY service (werbos, nginx, watchtower, beacon, warden), which means nginx picks up the new dashboard SPA. Regression test asserts the rendered command does NOT contain the single-service forms — catches the bug if anyone tries to re-filter for “performance.”

This was the day’s most useful learning. The fact that “dashboard lives in nginx” wasn’t surfaced anywhere. I learned it by hard-refreshing, seeing stale content, tracing through the compose file. The fix is now durable for every future operator who hits the same trap.

Numbers worth keeping

  • 11 commits (88ffb0c..4fdf6c6).
  • +62 net new tests across kanban, studio opt-in, MCP info, paths helper, updates.
  • Three new dashboard surfaces: kanban Pending, MCP page, Settings Updates row.
  • Four new sprint docs / artifacts: NX.1, MCP.1, IDEAS.md, GO-LIVE.md.
  • 2 names reserved: werbos on npm (0.0.0 stub ready; gated on auth tomorrow) + on PyPI (held by Sprint NX.1’s publish-ownership).
  • 3 places where API responses no longer leak /Users/<op> paths.

AHA

Distribution work doesn’t look like building. It looks like tightening.

The pattern:

  • Default OFF for power features. Power users add a flag.
  • Read-only first. Show the command; don’t run it.
  • One source of truth, hand-edited. mcp.toml, agency.yaml, user.md. The dashboard renders; it doesn’t write.
  • Surface the leak. Every operator-facing string is a potential leak. tilde_path is small; reviewing every endpoint for it is the real work.

Aphorisms

  • Distribution work doesn’t look like building. It looks like gating, defaulting, surfacing, naming.
  • Default OFF for power features. Power users add a flag.
  • Read-only first. Show the command; don’t run it.
  • Every operator-facing string is a potential leak.
  • The class of bug never fully closes; you just catch it at a tighter granularity.

Quote of the day

“this is good for now… tomorrow we will remove any direct path for my computer like ‘/Documents/Companies/werbosLLM/ werbos/’…”

A ninth sweep of the same fix: paths leak. tilde-collapse fixed /Users/<op> over HTTP. The remaining ~/Documents/Companies/ werbosLLM/werbos/ still reveals the operator’s preferred directory structure. Tomorrow’s sweep tightens the granularity one more click.

Carryover

  • /Documents/Companies/werbosLLM/werbos/ cleanup — the next tilde-fix tier. Project-layout still leaks in rendered commands; needs a different abstraction.
  • npm publish — blocked on auth (passkey on PC; recovery code not working). Operator generates an Automation token tomorrow + pastes into ~/.npmrc.
  • AG.1 sprint — agency router. Picks up after MCP.1.
  • EN.1 sprint — three decisions still to lock.
  • AG-SIG sprint — agent record signature checking.
  • max_iterations for orchestrators — default 4 too tight for the studio chain.