Skip to content

feat: leadline, what your code did in development, as an installable plugin with a box-side helper - #56

Merged
sean-brydon merged 9 commits into
mainfrom
feat/leadline
Oct 12, 2026
Merged

sean-brydon merged 9 commits into
mainfrom
feat/leadline

Conversation

@sean-brydon

Copy link
Copy Markdown
Collaborator

What you get

Leadline as a plugin you install from Settings → Plugins → Browse (not built in). On a worktree it adds four tabs:

  • Queries: every statement sent to a local Postgres, grouped by shape, with calls, timings and N+1 detection
  • Traces: each request as a waterfall with its SQL and the logs written during it
  • Logs and Endpoints, each with a detail view
  • Send to the agent from a slow query, an N+1 or a failing endpoint: the problem written up, shown for editing, then sent to the worktree's agent

A Leadline screen turns it on or off per box. Nothing leaves the box.

How it works

  • A plugin can bring a helper program. Its berth-plugin.json names the program, its arguments and a checksum per platform. The manifest is already covered by the consent hash, and the consent dialog now says the plugin can install and run a program on your boxes, naming it.
  • The laptop downloads the helper for the box's platform, checks size and SHA-256 against the manifest, and uploads it over Shipyard's own connection, so a box needs no internet.
  • berthd stores it, re-checks the hash at every start, starts it before any service, restarts it on a crash, stops it on shutdown, and relays its API to the app. When a worktree service starts, the helper is asked for the tracing settings and a DATABASE_URL through its proxy; a helper that is down or slow changes nothing.
  • berthd stays small: go list -deps ./cmd/berthd ./cmd/berth | grep leadline/ prints nothing.

How it was checked

  • go vet ./... and go test -race ./...: all packages pass on the merged branch (no Go changed after that run).
  • App: typecheck:plugins, check:titles, check:csp, check:themes, check:marketplace, unit tests (340 pass), pnpm build, npx tsc -p e2e; docs build.
  • Browser, mock mode: leadline.spec.ts, marketplace.spec.ts and the matching a11y scenes, 36 pass.
  • Live, on a throwaway fleet on a Mac: a real berthd and berth agent with their own homes, a local registry, the built app driven by Playwright. Leadline was installed from Browse, allowed, turned on for the box; a service started by berthd sent real traces and logs and ran statements through the proxy; all four tabs showed them; a repeated statement was flagged N+1 and each query was counted once; then Turn off and Remove data.

The live run found that, as first merged, no panel could read anything from a real box (the request path to the helper was one segment short and the 404 was reported as an old berthd). That and four smaller mismatches are fixed in the last five commits, with tests, and the mock now answers only what a real box would.

Not covered

  • A real Postgres. Docker did not answer on the test machine, so the proxy ran against the repo's fake Postgres with a real pgx client. SCRAM auth, TLS negotiation and real timings are untested.
  • Linux boxes were not run live; Linux is covered by the Go tests only.
  • Background services on a Mac box (launchd): the attach step sits before that split, but only a terminal service was run live.

Before it works for anyone

No helper is released. scripts/release-leadline-helper.sh builds the four binaries, uploads them to a leadline-helper-v0.1.0 release (not marked latest) and fills the listing's checksums. It has never been run. Until then the listing has no builds and every box says it can't run Leadline.

Open decisions

  1. Turning a plugin off, or uninstalling it, leaves its helper running on boxes; only the Leadline screen or berthd helper rm stops it.
  2. Nothing tells the helper a worktree was removed, so its database proxy stays open (the helper caps at 200).
  3. The helper's API has no authentication; it listens on loopback only.
  4. internal/box tests take 6 to 10 minutes under -race, near Go's 10-minute per-package limit.

Left out

A database editor, comparing a worktree against main, metrics, Leadline's overview charts, and a Home widget.

🤖 Generated with Claude Code

sean-brydon and others added 9 commits October 12, 2026 00:29
…n a worktree

Leadline is a plugin in the listing (Settings → Plugins → Browse), not
built in. It adds Queries, Traces, Logs and Endpoints panels to a worktree,
read from berth-leadline, the helper its manifest carries for boxes, and a
Leadline screen that turns the helper on, off and updates it per box.

- berth.helper in the plugin SDK: status, install, remove and request, bound
  to the calling plugin's own id (/v1/plugins/{id}/helper on the laptop
  agent, helpers/{plugin}/api/ through a box).
- The consent dialog and a plugin's row in Settings say, by the program's
  name, that a plugin with box.helper can install and run it on boxes.
- Send to the agent writes a slow query, an N+1 or a failing endpoint up,
  shows the text to be edited, and sends it to the worktree's agent or
  starts one. Statements are written by shape, never with values.
- Chart in @berth/plugin/ui: the app's own chart view for plugins.
- Mock mode runs the listing's bundle, installed and allowed, on fixtures
  captured from the real helper (scripts/capture-leadline-fixtures.mjs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and runs on a box

A plugin's berth-plugin.json may declare "box.helper": a program, how to
run it, and the address, size and SHA-256 of its build for each platform.
The manifest is covered by the plugin's consent hash, so the checksums are
what the person allowed.

berthd (internal/box/helpers*.go):
- keeps a helper in <state>/box/helpers/<plugin>/ (bin/<version>/<name>,
  data/, install.json, helper.log), takes it only from a paired laptop, and
  only if the bytes are the size and SHA-256 sent; checks the file again
  each time it starts it
- starts installed helpers before it serves anything, starts one again when
  it exits (1s, 2s, 4s … a minute; "failed" after three in a row), stops
  them with itself, and ends any an earlier berthd left running
- GET /v1/helpers, GET|PUT|DELETE /v1/helpers/{plugin}, GET …/log, and
  ANY /v1/helpers/{plugin}/api/…, relayed to the helper's loopback address
  with that as the Host and no Origin; 409 helper_off when it is not running
- before a worktree's service starts, asks each running helper with an
  attach path and merges the variables it answers, within the manifest's
  attach_env; a helper that is down, slow or wrong changes nothing
- berthd helpers, berthd helper log|rm (and berth … BOX)

laptop agent (internal/agent/helpers.go):
- GET|POST|DELETE /v1/plugins/{id}/helper?box=B for a plugin that is
  installed and allowed as it is now: downloads the build for the box's
  platform, checks it against the manifest, and uploads it over Shipyard's
  own connection

berth-leadline:
- POST /api/attach: the attach helper and the proxies in one call
- a helper started on port 0 comes back on the port it had (listen.json),
  so what a dev server was told still reaches it after a restart

listing build:
- build-marketplace.mjs carries box.helper into the listing, fills the
  assets of a helper built in this repository from its released builds,
  and no longer empties site/plugins/ when a build fails
- scripts/release-leadline-helper.sh builds, lists and uploads the four
  builds of berth-leadline to a release of their own (not run here)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
berth.helper.request("devl", "GET", "queries") asked the box for
helpers/<id>/api/queries. berthd relays everything after helpers/<id>/api
as the path on the helper, and berth-leadline's API is under /api/, so on a
real box every panel read /queries, got the helper's plain 404, and the
laptop agent reported that as "runs an older berthd that doesn't have this
yet" under a helper that was running. Mock mode answered either path.

The request now goes to helpers/<id>/api/api/<path>, as the SDK's own words
say ("asks the helper for /api/queries") and as the box's test with the
real helper asks. The mock answers only what a real box and helper would.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Against a real agent and box, the plugin misread three answers that mock
mode had answered differently:

- "off" is all a box says of a helper it does not have, whether it never
  had it or it was turned off; "none" is a plugin that names no helper. The
  plugin took "none" for never installed, so a box that had never run
  Leadline was told that what it recorded before is kept.
- "unsupported" is two things: a berthd that cannot run helpers (it never
  says its platform), and a platform the plugin has no build for. The row
  sent both to Settings -> Boxes to update the box, which is what every box
  shows while a helper has no released builds. A box with no build now says
  that, and nothing about updating.
- A download that fails or does not match its checksum is refused by the
  agent (502) and leaves the box off; "failed" is a program that is on the
  box and will not start. The confirm already shows the refusal.

The mock now answers each of these as the agent and berthd do, with a
scenario for each, and the SDK's types and docs say what the states mean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a helper is removed the box answers with the services it started
while the helper was on and that are still running ("attached"): their
telemetry endpoint and their database URL are the helper's, and nothing
restarts them. The app threw that answer away (berth.helper.remove
resolved with nothing), and the screen said only to restart "any service
that was started while it was on".

remove() now resolves with the status the laptop answers, attached
services included, and Leadline says which to restart: in a notification,
and on the box's row until it is turned on again. Removing its data says
the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ffline

The laptop agent answers a helper's failed download, a checksum that does
not match and a helper that did not answer the relay with 502 and a code of
their own (helper_download, helper_mismatch, helper_unreachable). Leadline
shows the agent's sentence itself, but anything that reaches the app's own
explanations took a 502 for "can't reach the box".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… is removed

Turning Leadline off and then removing its data asked the box twice, and
the second answer names no service: the helper was already gone. The row
took that for "nothing to restart" and dropped the note, while the service
went on pointing at the helper. An answer with no services now leaves what
the row already knew; turning Leadline on again is what clears it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
berth Ready Ready Preview Oct 12, 2026 12:31am UTC
berth-docs Ready Ready Preview Oct 12, 2026 12:31am UTC

Request Review

@sean-brydon
sean-brydon merged commit f200f8d into main Oct 12, 2026
7 of 8 checks passed
@linear-code

linear-code Bot commented Oct 12, 2026

Copy link
Copy Markdown

ENG-5902

This branch was successfully deployed

2 active deployments
Preview – berth-docs — 43797fdf Deployed Oct 12, 2026 by vercel[bot]
Preview – berth — 43797fdf Deployed Oct 12, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant