Day 18 — 'the surface tightens — kanban, opt-in, MCP, update flow.'
Day 17 closed with a near-zero security verdict + the pipeline indicator shipped. Day 18 wasn't another build day; it was a distribution-prep day. The arc: stop assuming werbos = my machine, my preferences, my path layout, and start preparing the surfaces a stranger would hit. Shipped: /pending → 4-column kanban, design-studio as opt-in (--studio default OFF), NX.1 npm-wrapper preflight + werbos name reservation, MCP.1 sprint doc + /dashboard/mcp page + /api/mcp/info, tilde-path collapse so API responses don't leak /Users/<op>, Settings → Updates section + /api/updates/check, and a docker upgrade-command fix that caught the dashboard-lives-in-nginx bug. Plus IDEAS.md (12 features) + GO-LIVE.md (v1 release punch list) as durable repo-root scratch. 11 commits.
“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:
/api/studio/*routes — not mounted when off--tools image_generate(orscreenshot_url) without--studioraisesSystemExitwith a helpful pointer- Dashboard
/studionav link — hidden via the newfeatures.studiofield on/api/settings - 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:
werbosis FREE on both npm AND PyPI today. Squat risk if we sit.- Node 22.15.1 + npm 11.10.1 on operator’s box.
uvnot 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 serveexposes. 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:
- OS family —
/Users/<x>says macOS,/home/<x>says Linux,C:\Users\<x>says Windows. - 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:
werboson 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_iterationsfor orchestrators — default 4 too tight for the studio chain.