Skip to content

v2.1.0 — catalog management in the cockpit

Choose a tag to compare

@chiam-ck chiam-ck released this 22 Jul 05:10
· 18 commits to main since this release

Catalog management reaches the cockpit. Offering CRUD was previously CLI-only (bss admin catalog *) and browser-only (the v1.6.1 CSR Catalog screen) — the conversational surface didn't have it. It does now, under propose-then-/confirm.

Highlights

  • Four verbs on the operator LLM surface. catalog.add_offering, catalog.add_price, catalog.window_offering, and a new catalog.retire_offering. The first three were registered and in the operator_cockpit profile since v0.7 but sat in LLM_HIDDEN_TOOLS, so the model never saw them; retire had no tool at all.
  • /confirm replaces invisibility as the gate. A strictly stronger control: the operator reads the staged args before the write lands, where hiding just meant the capability didn't exist. No catalog write has a one-call undo — add_price can only be superseded, never removed, and list_active_offerings feeds order creation, so a bad row is sellable the instant it lands.
  • Multi-turn elicitation — the agent asks, it doesn't assume. Ask for "a new 5GB plan" and it comes back for the plan id, price, and allowances rather than inventing them.
  • Never customer-facing. Not in customer_self_serve; asserted in both directions.

Elicitation, and why it's enforced twice

The orchestrator emits a permissive function schema (additionalProperties: true), so the description prose is the model's only arg contract. Both layers matter:

  1. The four tool descriptions are explicit "ask, never assume" contracts — never derive a plan id from the name, never price by analogy with an existing plan, never assume allowances typical for a tier.
  2. tools::require_fields gates server-side, naming every absent field at once so the model asks one question instead of probing. add_offering additionally rejects a plan that grants no allowance (pass 0 for a deliberate omission); window_offering rejects both bounds absent, treating an explicit null as a deliberate clear.

An elicitation bounce is reported with is_error = false (ToolError::is_elicitation) so clarifying questions don't burn the 3-strike failure-bail budget. The first implementation assumed "absent from FAILURE_MARKERS" was sufficient — but is_failure_result is is_error || markers and every Err sets is_error, so three clarifying questions in one turn killed the turn with a "couldn't recover" panel. Caught by the new loop test, not by review.

Documentation fixes

  • HANDBOOK §8.1 gained the chat path alongside the CLI and browser routes.
  • HANDBOOK §8.1a was actively wrong and is removed. It claimed bss admin catalog add-offering had no --data-roaming-mb flag (v0.20 added it) and told operators to run raw INSERT INTO catalog.bundle_allowance — bypassing the policy layer, a doctrine violation — with a "recommended" fallback pointing at a Python package deleted at v2.0.0.
  • Knowledge index reindexed. It had been stale since 2026-07-19, predating the v2.0.0 release, so knowledge.search was serving pre-2.0 content. Also note: retrieval is Tier-0 Postgres FTS with plainto_tsquery, which ANDs every term — a question containing one out-of-corpus word returns zero hits, not a near miss. Retire/decommission/withdraw/remove synonyms were added to the docs for that reason. The provisioned pgvector column remains unpopulated (the Tier-1 embedder pass is unbuilt).

Compatibility

SemVer minor, backward compatible. No wire contract, event, payload, or DB schema change. BSS_RELEASE 2.0.0 → 2.1.0.

Gate

cargo fmt --all --check · clippy -D warnings (0) · cargo test workspace 540 pass / 0 fail · 14 doctrine guards.

The hero scenario suite was not run for this cut — it requires mock payment/KYC providers and the deploy carries a live BSS_PAYMENT_PROVIDER=stripe. The change is confined to the LLM tool surface, which the hero flows don't exercise; it was verified live against the running stack instead (tool visible, elicitation asks rather than assumes, destructive gate held with nothing written, retire endpoint reachable, knowledge.search retrieves and cites the new content).

See DECISIONS 2026-07-22.