Native Rose onboarding and staged knowledge¶
The CLI provides staff setup, website discovery and review, private scraping and
cleaning, local/Drive imports, explicit publication and controlled canonical sync.
New commands execute Python from the current checkout by default. They require
the matching database migrations and configured providers. Native mutations
currently accept only --env test; execution location does not isolate data.
The Supabase target does: see isolated test onboarding
to onboard without touching production Supabase.
One-command onboarding¶
rose-tenant onboard example.com --name "Example" --env test --dry-run
rose-tenant onboard example.com --name "Example" --env test --yes
rose-tenant onboard example.com --name "Example" --env test --until discover --yes
rose-tenant onboard example.com --env test --resume RUN --until sync --yes
rose-tenant onboard example.com --name "Example" --from ./documents --env test --yes
The default target is sync. Onboarding creates missing registration, bootstraps
minimum settings, automatically accepts discovery recommendations when proceeding
beyond discovery, prepares content, publishes it and indexes into test. Its single
approval covers shared registration/configuration and, when requested, shared
publication. Existing customer settings and public display state are preserved.
--until create|bootstrap|discover|scrape|clean|sync stops at that checkpoint.
Setup is synchronous; repeating the domain resumes idempotently. After discovery
or import is accepted, the database stores the target. Local execution advances
only while the foreground command is alive. Remote recovery can advance without
the CLI; --execution remote --no-wait returns after acceptance. Repeat the same command to
reattach; use --resume RUN to select an existing run explicitly. A new source
refresh requires a new --idempotency-key. An interrupted local upload must be
finished with the original source and submission key before progression.
Stopping at discovery requires manual review before resuming further. Failed runs
require retry; resuming does not silently retry failed work or alter a selection.
A selected page returning 404/410 is dropped from the selection without retries;
the rest of the run continues and publishes the remaining pages. The scrape work
item keeps the fetch_status and removal_reason, and review --export shows the
page as excluded.
Execution and connections¶
Local execution uses the same environment loading as rose-chat, including local
Supabase overrides. It calls shared command services and content stages directly,
and starts loader subprocesses with the checkout's Python interpreter. It needs
no Knowledge API/content-worker server, Cloud Tasks, Scheduler, Cloud Run lookup,
or OIDC impersonation. Providers and immutable GCS artifacts remain remote.
Install backend dependencies with the usual Poetry/bootstrap workflow and configure
service-role database credentials, GCS ADC, Firecrawl for website acquisition,
and the OpenAI (EU) key for page classification/cleaning. Loader model credentials follow the
existing ingestion configuration. The CLI never installs schemas or starts databases.
Firecrawl is a standalone Google Secret Manager secret, separate from the backend
environment bundles. When FIRECRAWL_API_KEY is not set, a local website run reads
it from Secret Manager with your Application Default Credentials; this requires
Secret Manager access. An exported FIRECRAWL_API_KEY takes precedence.
rose-tenant test-connection --env test --json
rose-document-loader discover example.com --env test --execution local
rose-tenant onboard example.com --env test --execution remote --yes --no-wait
Local target validation accepts three shapes:
- shared Supabase with remote MongoDB, Neo4j and Redis;
- a complete, explicitly configured loopback stack coordinated by local Supabase on port 54321;
- a Supabase preview branch wired by
./bootstrap.py --branch, which also writes the branch's isolated retrieval namespace (MONGO_DATABASE=<slug>_test,IX_TENANT_PREFIX=<slug>__) tobackend/.env.local. This is the same namespacejust cloud-previewuses for that branch.
Any other Supabase override with shared remote retrieval is rejected: it cannot
coordinate that corpus independently. A preview branch writes no production
Supabase rows and no shared test Mongo documents. Its Neo4j nodes and Redis keys
are isolated only logically, by tenant prefix, on the shared test instances.
No listening service changes selection automatically. Before mutations the CLI displays safe
target identities. Runs bind their execution and target fingerprint; changing
credentials is allowed, changing a saved target is rejected.
New work defaults to local. Continuations infer the saved mode; existing rows
migrate as remote. An explicit conflicting mode fails. Local processing rejects
--no-wait before submission. Ctrl-C exits 130, joins owned work and releases its
attempt only after it has stopped. Completed stages and publication receipts
remain durable. Resume the saved run with its original configuration; use retry
for failed preparation work. A zero subprocess exit without durable completion
is an error. Interrupted local work is never dispatched to the cloud. Hard process
loss may require waiting for its recorded lease to expire before explicit resume.
Scheduled recovery reclaims expired local loader attempts to release shared
capacity, preserving their saved targets and retry budgets. It does not run their
retries or recover local content/preparation stages. A surviving loader child with
a healthy heartbeat retains its slot even if its parent has exited.
For remote mode, use gcloud auth login and gcloud auth application-default login.
The CLI discovers knowledge-api-test and admin-api-test, and mints an OIDC
token through impersonation. The operator needs service description access and
iam.serviceAccounts.getOpenIdToken; the admin account must be an API invoker.
Remote preflight checks matching API/worker protocol and CLI/API/loader database
identities. Explicit KNOWLEDGE_API_URL, KNOWLEDGE_API_OIDC_AUDIENCE, and
KNOWLEDGE_API_IMPERSONATE_SA retain the same checks. There is no target fallback.
test-connection also checks MongoDB and Neo4j connectivity. It does not run the
knowledge integrity diagnostic. A subsequent rose-chat invocation must use the
same database configuration to verify retrieval.
Isolated test onboarding on a preview branch¶
Use this to onboard, scrape or import a client into a test knowledge base without
writing production Supabase or the shared test corpus.
- Confirm the exact domain and path before onboarding: a requested name can be wrong
(
sunday.appis unrelated tosundayapp.com; the UK path is/en-gb, not/gb-en). Check the client's existing workspaces read-only on production (public.workspacesjoined topublic.workspace_rootsby host) and itsbackend/apps/shared_data/prompts/website-agent/skills/clients/<domain>/folder, and reuse the production path and config key for a path workspace. - From the repository root, run
just target isolatedon a feature branch. It runs./bootstrap.py --branch, which writes the branch credentials plusMONGO_DATABASE=<slug>_testandIX_TENANT_PREFIX=<slug>__tobackend/.env.local, then writes the gitignored.rose-targetmarker. Local execution accepts a preview Supabase only with that pair; the--dry-runtarget must show the branch host, database and prefix, andjust targetprints the same line. While the marker exists, every CLI behaves as ifROSE_REQUIRE_ISOLATED_TARGET=1were set: if the wiring disappears (unwire, a teardown, another session),.env.testwould target the shared production project, and every local command refuses it. - Onboard or import with local execution as usual. New clients start with their domain enabled, so chat works as soon as the corpus is ready.
- Additional (path) workspaces are staff actions without a CLI: create them with
create_workspaceand enable their root as the branch admin, as the backoffice does. New additional workspaces start disabled, and a path root can only be enabled once the client'sexplicit_routing_enabledflag is on. Withpsqlon the branch (credentials inbackend/.env.local, never the shared project):
begin;
update public.clients set explicit_routing_enabled = true where name = 'Example';
select set_config('request.jwt.claims', json_build_object('sub',
(select id from auth.users where email = 'admin@admin.com'), 'role', 'authenticated')::text, true);
set local role authenticated;
select public.create_workspace((select id from public.clients where name = 'Example'),
'Example UK', 'example.com', '/en-gb');
update public.workspace_roots set enabled = true
where host = 'example.com' and path_prefix = '/en-gb';
commit;
Then discover it with rose-document-loader discover DOMAIN --workspace-id ID.
4. Verify with rose-chat --site DOMAIN --env test, adding --page-url of a page
under the workspace path to route to a path workspace, and with
diagnose-tenant --workspace-id. Local runs read the same branch wiring.
5. Run just target shared from the repository root when finished. It purges the
corpus with rose-tenant purge-preview --yes while .env.local still points at the
branch, unwires, and removes the marker. Then delete the branch
(python3.12 scripts/supabase_branch.py delete <git branch>).
To onboard the same way from the local backoffice, with every intermediate state
visible in the UI, start just dev-knowledge-stack test from backend/ on the
isolated target. See testing knowledge changes.
Local submission receipts are bound to their target: rerunning a domain on a new
branch needs a new --idempotency-key. An imported folder keeps its
.rose-source.json; import other content from another folder or reuse its source ID.
Neo4j and Redis isolation is logical (tenant prefix on the shared test instances).
Register and configure¶
rose-tenant create example.com --name "Example" --env test --dry-run
rose-tenant create example.com --name "Example" --env test --yes
rose-config bootstrap --site example.com --env test --dry-run
rose-config bootstrap --site example.com --env test --yes
rose-tenant status example.com --env test
Registration creates an onboarding client and an enabled domain, so the new
client can be tested in chat and the widget immediately. It does not grant customer account membership or start acquisition.
Repeating registration preserves identity; conflicting ownership fails.
Bootstrap validates and fills missing company identity, language, and retrieval tier. It preserves existing overrides, global tier defaults, source migration flags, widget state, CTAs, tone, and qualification settings. Locked configs and changes since the displayed preview are rejected. Minimum bootstrap does not replace client-specific onboarding, prompt work, or commercial setup.
Registration and configuration write the configured Supabase. Without a
preview branch that is the
shared production project: selecting test does not create a private client sandbox. The commands display the proposed
scope before writing. Interactive commands prompt; unattended writes need
--yes.
Sync existing sources¶
rose-document-loader sync example.com --env test --dry-run
rose-document-loader sync example.com --scope all --env test --yes --wait
rose-document-loader sync example.com --scope website --env test --execution remote --yes --no-wait
rose-document-loader sync example.com --scope documents --env test --yes
rose-document-loader sync example.com --scope faqs --env test --yes
One database transaction records the request and creates the necessary child
operations. all uses separate scrape_docs_only, docs_only, and faq_only
loader work. It performs no crawl, source publication, full reindex, or tier
migration. Current scopes and never-populated absent scopes are explicit
no-ops. Existing environment-wide loader admission, fencing, and recovery
remain authoritative. Remote recovery dispatches committed remote work after the
terminal closes. Local work advances only in the foreground, through the same slot.
The default waits for every requested scope. A failed or partially failed
scope fails the command. Remote --no-wait reports durable acceptance, not indexing
completion. --timeout stops owned local execution; it only bounds waiting in remote mode.
Legacy update-tenant and update-docs-tenant still write directly. They warn
about overlap and refuse when KNOWLEDGE_CONTROL_PLANE_REQUIRED is set locally.
They are rollback tools, not controlled sync aliases.
Prepare and inspect a website¶
rose-document-loader discover example.com --env test
rose-document-loader review RUN --env test --export selection.json
# Edit only the included booleans in selection.json.
rose-document-loader review RUN --env test --apply selection.json
rose-document-loader scrape RUN --env test
rose-document-loader export RUN --stage raw --output ./raw --env test
rose-document-loader clean RUN --env test
rose-document-loader export RUN --stage clean --output ./clean --env test
rose-document-loader sync example.com --run RUN --env test --dry-run
rose-document-loader sync example.com --run RUN --env test --yes
Discovery may fetch small classification samples. It stops before full-page
scraping and exposes the complete paginated candidate inventory with reasons.
Review files bind item IDs and decisions to one run and selection revision; they
cannot substitute URLs. scrape --accept-recommendations explicitly accepts the
recommendations without a review file. Scrape and clean stop at their own stages.
Preparation artifacts stay private in GCS and never create canonical documents,
snapshots or corpus freshness. Automatic source sync cannot ingest them.
Exports use item-ID filenames, verify hashes and record outcomes in index.json;
missing or failed selected artifacts return a nonzero exit.
sync --run atomically checks source generation, selection and canonical document
baselines, publishes up to 1,000 selected documents, and admits scoped loader
work. It preserves excluded/deleted policies and does not delete missing pages.
A newer source or document edit requires a fresh preparation run. Repeating
publication returns the same recorded publication and operation identities.
Published sources are shared across environments. Test selects the immediate
retrieval destination; other environments can later ingest the published content.
Import local or Drive documents¶
rose-document-loader import example.com --from ./documents --env test --dry-run
rose-document-loader import example.com --from ./documents --env test --idempotency-key import-one
rose-document-loader import example.com --from https://drive.google.com/drive/folders/FOLDER --env test
rose-document-loader clean RUN --env test
rose-document-loader sync example.com --run RUN --env test --yes
Imports accept Markdown, UTF-8 text, PDF and DOCX; Drive Google Docs export as text. Limits are 1,000 files, 20 MiB per file, and 100 extracted pages per binary document. Unsupported files are reported together before acceptance. Legacy website scraping folders require their existing migration workflow.
Local imports persist a portable .rose-source.json source identity alongside
the files (or accept --source-id), freeze relative paths and hashes, and upload
all originals before extraction is accepted. Keep that marker when moving the
source. Repeating the same key resumes missing uploads; a new key refreshes the
same canonical file identities. Unchanged files reuse accepted extraction and
cleaning artifacts. Original binaries are retained at publication.
Drive uses Rose's read-only document-loader service identity. Share the folder with that identity; no browser token is copied. The API freezes the inventory, and workers check each file's version before and after acquisition. Changes or inaccessible files fail visibly rather than producing a partially ready run. After import, raw text is ready; clean and publication remain separate commands.
Inspect and recover¶
rose-tenant operation OPERATION --env test --follow --timeout 900
rose-tenant operation OPERATION --env test --items --json
rose-tenant status example.com --env test --json
rose-document-loader status RUN --env test --json
rose-document-loader retry RUN --env test
rose-document-loader cancel RUN --env test
rose-document-loader diagnose-tenant example.com --env test
rose-chat "What does your product do?" --site example.com --env test --trace
Source freshness and successful loader completion do not prove answer quality.
Use the existing diagnostic for storage integrity and an actual question for
chat verification. Follow all pagination cursors in raw API consumers; the CLI
operation --items command reads every item page.
For ingestion-to-chat verification, inspect the cleaned export and choose a distinctive fact from the newly ingested content. Ask about it in a fresh chat session without including the answer in the question. Verify the retrieved passage belongs to the new source; a generic answer can come from older knowledge.
Submissions save their idempotency key or selection-revision receipt under
~/.cache/rose/submissions/ before submission. These files are private local
receipts, never workflow state. Ambiguous network failures reconcile with the
same key. Reconnect with --idempotency-key KEY; bootstrap retries reuse the
saved config proposal. A key bound to different scope is rejected. Keep the
receipt when retrying from the same machine. Ctrl-C detaches with exit 130;
accepted operations continue.
Retry preserves successful items and historical attempts. Failed loader work reuses the existing publication; it does not republish. Cancellation stops remaining work and cannot undo already-published canonical content. Tenant status includes the latest preparation checkpoint, active operations and corpus freshness; these are independent of a structural retrieval audit.
Automation output¶
--json writes one object to stdout:
Errors contain code, message, and next_action, plus submission information
when a submitted request could not be confirmed. Progress and write summaries
go to stderr. Raw gateway errors, credentials, and execution fences are omitted.
| Exit | Meaning |
|---|---|
| 0 | Success, durable acceptance, or requested setup checkpoint |
| 1 | Execution/partial failure or unknown outcome |
| 2 | Invalid input, configuration, or access |
| 3 | Prerequisite/conflict or human acceptance needed |
| 4 | Local wait timeout |
| 130 | Local interruption; accepted work continues |
Existing diagnostics retain their existing exit meanings. Dry runs do only validation and reads: no durable request, upload, crawl, extraction, or cleaning.
Deployment and verification¶
Apply the additive migrations through the normal deployment process, then deploy
the compatible API/content worker and CLI. The deployment recipe grants the API
worker-invoker access for readiness checks, original-upload storage access, and
API/worker access to the existing read-only Drive credential secret. The API
advertises staged_workflow only when the database supports the complete workflow,
the serving worker reports preparation protocol 1, and the loader target matches.
Drain or cancel preparation work before reverting to an older runtime.
No migration or runtime was deployed during implementation.
The disposable control-plane SQL acceptance runner verifies new registration, bootstrap preservation and stale baselines, canonical sync no-ops, idempotency, loader modes, private checkpoints, upload completeness, stale leases, retry, source-baseline conflicts, original-file preservation, privileges and migration replay. Python tests cover the CLI, API, existing workers, repositories, config models, admin compatibility, and fenced loader. A deliberately authorized live onboarding case remains required for release acceptance after deployment, including actual question-based chat verification. Local fixtures do not certify live answer quality.