/client-new — a thin operator slash command over the control-plane CLI
0039-client-new-operator-command
- Reversibility
- two-way door
DEC-0039 — /client-new operator command (thin wrapper over the control-plane CLI)
Reversibility: two-way door — the command is a thin convenience surface over the CLI (its conventions/defaults are swappable without touching the pipeline); the durable parts are the control-plane CLI it drives and the one-client-one-repo isolation it inherits.
Context
Extraction runtime architecture — the moat set the goal that "one command stands up a client's loop" (the Time-to-Live-Wiki metric), and Runtime orchestration & per-tenant control plane — the learning loop becomes a runnable system built the dossier-runtime control plane that realizes it (provision → ingest → extract → emit → commit, siloed per tenant). Subsequent work made that loop keyless and runnable on a subscription — extraction via the Claude Code CLI (Subscription-backed extraction is a first-class transport — ClaudeCodeClient (no API keys)), keyless web ingest via HttpConnector with Firecrawl as the keyed upgrade (Web ingestion — a keyless HttpConnector by default, Firecrawl wired as the premium path, and a first-class CLI web-ingest mode) — and protected its sovereignty guarantee under nesting (Fix git-per-tenant isolation when a tenant root is nested inside another repo). What was missing was an operator-facing entry point: standing up a client still meant remembering and assembling the right dossier-runtime invocation by hand. This decision adds the missing one-command surface.
The artifact is .claude/commands/client-new.md — a build-side Claude Code slash command. It is explicitly a thin wrapper: it derives a slug and dispatches to node packages/runtime/dist/cli.js (the dossier-runtime bin), which owns provisioning, ingest, extract, emit, and commit. It does not re-implement the pipeline and does not generate a bespoke per-client harness script — the CLI is the single source of truth.
Options considered
- A bespoke onboarding script generated per client — author a standalone harness each time a client is stood up. Rejected: it forks the pipeline logic out of the control plane, breaks single-source-of-truth, and re-creates by hand the exact manual-staging footgun Web ingestion — a keyless HttpConnector by default, Firecrawl wired as the premium path, and a first-class CLI web-ingest mode / Fix git-per-tenant isolation when a tenant root is nested inside another repo were built to retire.
- No command — keep invoking
dossier-runtimeby hand — rely on operators remembering the right flags. Rejected: the realistic agency motion ("point Dossier at a client's site") deserves a first-class, reproducible surface, not a remembered incantation; raw flags are error-prone (wrong root, forgotten--subscription, unbounded--pages). - A thin slash command that drives the existing CLI (chosen).
/client-newderives a deterministic slug and composes exactly the rightprovision/runinvocation, branching on its argument. The CLI stays the single source of truth; the command is convenience + convention only.
Decision
Add /client-new as a thin operator command over dossier-runtime, with two branches on its argument.
- Empty args → scaffold only (
provision). Runsdossier-runtime provision --root clients/<slug>/tenants --client <slug> --vertical digital-experience-agency, standing up a siloed tenant:clients/<slug>/tenants/<slug>/{ okf/ (a git repo), dossier.tenant.json }. - A URL → scaffold AND learn (
run). Runsdossier-runtime run --root clients/<slug>/tenants --client <slug> --vertical digital-experience-agency --url <URL> --pages 1 --subscription, which does an idempotent provision then the loop: keylessHttpConnectorcrawl (provenance = the live URL, no API key) → subscription extract (claude -p, noANTHROPIC_API_KEY) → emit OKF → git commit into the tenant's own repo. - Conventions it establishes (the "why" worth capturing):
- One client = one
clients/<slug>/workspace, tenant root atclients/<slug>/tenants/<slug>/— a stable, legible layout for the dogfood and for real engagements. - Deterministic, filesystem-safe slug: slugify the client name for the empty case; for the URL case, slugify the host including the TLD (dropping the TLD would need a public-suffix list and wouldn't be deterministic). Lowercase; non-
[a-z0-9]runs → a single-; trim. Idempotent on re-run (existingclients/<slug>/is fine — provision/run re-affirm, never destroy). - Default vertical
digital-experience-agency(the DXA reference vertical), overridable. - Keyless + subscription is the default web path (
HttpConnector+claude -p); Firecrawl is reserved behind--connector firecrawl(needs a key) — the floor is free, the premium is opt-in (Web ingestion — a keyless HttpConnector by default, Firecrawl wired as the premium path, and a first-class CLI web-ingest mode). --pages 1default to keep the metered subscription extraction tight; raise only on request.
- One client = one
This operationalizes Extraction runtime architecture — the moat's "one command stands up a client's loop" into an operator-facing onboarding flow, without adding any new pipeline code.
Rationale
- Single source of truth, honored. The command re-uses the control-plane CLI verbatim; provisioning/ingest/extract/emit/commit logic lives in exactly one place (Runtime orchestration & per-tenant control plane — the learning loop becomes a runnable system). A thin wrapper can't drift from the pipeline because it is the pipeline's invocation.
- Keyless-by-default matches the GTM and the user's standing preference. The default URL path needs no API key (keyless crawl + subscription extraction), so an agency can point Dossier at a client's site with zero key management — the exact "delivered through the agencies that already serve them" motion of Dossier — Mission & North Star, reusing Subscription-backed extraction is a first-class transport — ClaudeCodeClient (no API keys) and Web ingestion — a keyless HttpConnector by default, Firecrawl wired as the premium path, and a first-class CLI web-ingest mode.
- Use the Claude primitive. A slash command driving an existing CLI is squarely Claude-primitives-first build strategy — a Claude Code surface over the loop, no bespoke onboarding infra.
- Inherits the sovereignty guarantee. Because it drives
dossier-runtime, each onboarding commits into the tenant's own isolated git repo — the Fix git-per-tenant isolation when a tenant root is nested inside another repo nested-repo fix protects exactly thisclients/<slug>/tenants/...-inside-the-Dossier-tree layout. verified, notasserted. The command was exercised end-to-end with real output, repo left clean: (1) the scaffold path provisioned a throwaway tenant → manifest (v1, vcs git, DXA vertical) + a realokf/git repo, then removed it; (2) the URL path ran--url https://en.wikipedia.org/wiki/Institutional_memory --pages 1 --subscription→ 4 atoms emitted, 3 edges linked, 0 rejected, committedfa5446dinto the tenant's own OKF git repo, withsource: https://en.wikipedia.org/wiki/Institutional_memory+confidence: inferredprovenance on disk; anexample.comrun was correctly clean (0 atoms — nothing to extract). This is an operator-convenience surface (a two-way door), soverifiedattaches to the command's behavior, not to broad multi-corpus validation of the underlying pipeline (which keeps the confidence levels of Subscription-backed extraction is a first-class transport — ClaudeCodeClient (no API keys) / Web ingestion — a keyless HttpConnector by default, Firecrawl wired as the premium path, and a first-class CLI web-ingest mode).
Consequences
- Operator onboarding is now one command.
/client-new(no args) scaffolds a tenant;/client-new <url>scaffolds and learns a site in a single step — the Time-to-Live-Wiki goal made operable. - No new pipeline code, no fork risk. The command adds zero extraction/ingestion/runtime logic; all behavior traces to
dossier-runtime. Changing the loop changes the command for free. - Known caveat, documented in the command. The command's "next move" note flags that
dossier-runtime site --buildhas a known dispatch bug and recommendsrun --siteinstead — see Fix dossier-runtime `site` subcommand dispatch (isSub omits 'site') (theisSubomission)./client-newitself drives onlyprovision/run, so it is unaffected. - Two-way door. The command's conventions and defaults (workspace layout, slug rule, default vertical/pages, keyless default) are swappable without touching the pipeline; the durable commitments are the control-plane CLI it drives and the one-client-one-repo isolation it inherits.
Review
No scheduled revisit — this is a thin convenience surface. Revisit if the control-plane CLI's provision/run flag contract changes (the command's invocations would need to follow), if multi-page or non-DXA onboarding becomes the common case (defaults would shift), or if the slug convention needs a public-suffix list for real-world hosts. Confidence stays verified for the command's verified behavior; the underlying pipeline's broader validation is tracked by its own decisions.