Skip to content

surfaces: every daemon route has a verb and a provider method - #94

Merged
radicalkjax merged 5 commits into
mainfrom
phase/close-the-surface-ledger
Sep 6, 2026
Merged

surfaces: every daemon route has a verb and a provider method#94
radicalkjax merged 5 commits into
mainfrom
phase/close-the-surface-ledger

Conversation

@radicalkjax

Copy link
Copy Markdown
Member

The contract work measured how far the project is from its own stated shape, where the daemon owns all state and each surface is a thin client: 76 routes had no command-line verb and 56 had no web provider method. This closes that to two entries, both a product decision rather than plumbing.

The first finding was that both extractors were undercounting. Each already normalised interpolated paths and stripped query strings, but neither pattern could match a call that carried one, and the provider extractor skipped the modules the provider delegates to. Fixing that closed about 29 phantom gaps before any feature code was written, which is a reminder that a check is only as good as what it can see.

  • 61 command-line verbs, including six new families: authentication, drift, executions, onboarding, workspaces and send, plus verbs across agents, bots, canvas, configuration, connectors, cooperation, formations, recipes, rules and safety. Every one goes through the daemon client and the one output helper. Three routes needed bodies the client could not send, so it gained a raw request path for plain-text configuration and a streaming post for the onboarding stream, and the file upload builds its multipart body by hand rather than pulling in another feature.
  • 17 provider methods in the shared web provider and its interface, so both apps can reach them. The desktop spreads the web provider, so it gains them without a second implementation.
  • The ledger is split in two. What remains a gap stays in the ledger; what should never have a verb moves to a separate list with a reason each: the single-page-app assets, the webhook endpoint third parties post to, the health and readiness probes on the provider side, the streaming half of chat, and six paths the command line must reach without a daemon, which are the diagnostics, the fixes, the panic wipe and travel mode. Those last ones matter: they have to work when the daemon is the broken thing.

What is left in the ledger is the author registry. Registering a signing identity happens before a daemon exists, so routing it through the daemon would make first-run signing depend on one, while leaving it as it is keeps a second writer against a registry the API owns. That needs a decision, and it is written down as one.

One rename: a rule verb called reassign tripped the test that bans an assign verb anywhere in the command tree, so it is now called move. The route is unchanged.

Documentation: a tutorial step that scaffolded from the deleted template system now applies a recipe, and the formations guide now shows the login flow and token commands instead of a command that printed a derived token. Two stale claims about the old token scheme remain in the reference pages and are worth a follow-up.

Verification: builds and clippy -D warnings clean; 95 tests across 14 binaries; cargo fmt clean; the surface check reports 123 routes against 111 command-line paths and 118 provider paths and passes; frontend typecheck, lint and build pass.

🤖 Generated with Claude Code

https://claude.ai/code/session_018M415coMKAv5V2xJQsaSf8

radicalkjax and others added 5 commits September 6, 2026 13:16
Both extractors already carried the normalisation the OpenAPI templates
need — `${id}` -> `{}` in provider-methods.mjs, `{id}` -> `{}` in
cli-routes.sh, and a `split("?")` for query strings — but neither regex
could ever match a literal carrying one, so every interpolated or
query-bearing call read as an uncovered route.

Widen both to accept `?`/`&`/`=` and `${...}` holes, strip the query
before comparing, and treat a hole as a path segment only when a `/`
introduces it (so `/executions${queryString(f)}` is `/executions`, not
`/executions/{}`). Also read the `web/api/*.ts` modules the provider
delegates to: those calls are the provider's calls.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018M415coMKAv5V2xJQsaSf8
…ve one

61 routes had no command-line verb. Adds them as new families (auth,
drift, execution, onboarding, workspace, send) and as verbs on the
families already there (agent states/step-autonomy, bot status/
formations/memory, canvas --connections, config connector/heartbeat,
ten connector verbs, cooperation utterances/recent, five formation
verbs, eleven recipe verbs, five rule verbs, safety disguise-profile,
healthcheck --ready). Every one goes through `Client` and prints
through `output::emit`.

Three routes needed a body the JSON client does not speak, so they use
`Client::request` directly: `/recipes/import` (text/plain TOML),
`/recipes/{id}/export` and `/render` (text/plain out), and
`/connectors/install-wasm` (a two-part multipart body built by hand
rather than pulling in reqwest's multipart feature). `/workspaces/
onboard` answers SSE over POST, so `Client::post_stream` hands back the
undecoded response.

Under /formations the drum rule holds: intents, members/eligible,
propose-intent, votes and run-command are composition, intent,
constraints, intervention and inspection. No assign verb.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018M415coMKAv5V2xJQsaSf8
…b methods

The gap ledger had 76 CLI entries and 56 provider entries. Most were
real; a handful were routes that should never have a verb or a method
and were sitting in a file that reads as a to-do list.

- `scripts/surface-not-surfaced.txt` is the new list for those, with a
  reason on every line: the SPA asset routes, the inbound webhook, the
  two process probes, the SSE half of chat, and the six offline-first
  CLI paths (doctor, fix, panic, travel) that must work when the daemon
  is the thing that is broken.
- `check-surface.sh` reads both files and still fails on a stale entry
  in either, so neither can grow quietly.
- 17 provider methods for the routes the web surface could not reach:
  session/token management, the three bot views, heartbeat config, both
  connector installs, recent utterances, data import/purge, rule drift,
  execution vacuum, formation propose-intent and votes, and sessions.
  The desktop provider spreads the web provider, so both apps get them.
- Two doc steps called verbs that do not exist: the deleted project
  scaffolder (now recipes) and `springtale api token print` (now the
  login flow plus `auth tokens`/`auth revoke`).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018M415coMKAv5V2xJQsaSf8
…groups

`reassign` tripped the drum-rule test that bans an assign verb anywhere
in the clap tree. A rule is re-homed onto another connector, never
handed to a named member, so `move` is both the honest name and the one
that keeps the ban absolute. The route is unchanged.

The formation group test gains the five new verbs with their groups:
`intents`/`eligible` are inspection and composition, `propose-intent`
and `vote` are the intent group, and `run` is the execution half of
`commands` — the same split check-surface.sh already enforces on the
routes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018M415coMKAv5V2xJQsaSf8
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018M415coMKAv5V2xJQsaSf8
@radicalkjax
radicalkjax merged commit 7e31670 into main Sep 6, 2026
30 checks passed
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