# Phase 03.1 Live Lessons

Use these lessons when the customer bootstrap reaches Slack, profile, gateway,
or live-verification stages.

## One production browser path

Browserbase direct CDP from the VPS `sellable-admin` profile is authoritative.
Use the persisted `slack` Browserbase context. Do not use local Chrome, a local
browser session, an alternate cloud-browser harness, a Slack developer CLI
ticket, copied challenge, or manual Developer Console work.

The reusable controller is the packaged
`runtime/scripts/browserbase-cdp-agent.mjs`. It exposes sanitized navigation,
inspection, click, fill, rich-editor type, keypress, wait, and release actions.
It serializes commands so adaptive agents can inspect and self-correct without
racing the page.

Hermes terminal calls have a short execution ceiling, so live UAT must start
the controller in the background with `--context slack --control-dir` pointing
to a private mode-0700 directory inside the `sellable-admin` profile. Wait for
`state.json`, then write each mode-0600 JSON command atomically into `inbox/`
and read the matching response from `outbox/`. This preserves one Browserbase
session across as many inspect/click/type/wait decisions as needed. Always send
a final `{"action":"release"}`, verify its response says `released:true`, and
remove the temporary control directory after copying only its redacted session
and context fingerprints into the UAT receipt. Never launch the bare controller
as a foreground terminal call; the terminal timeout will kill the live session.

## Authentication

Probe the persistent context before requesting any factor. Accepted context
reuse records zero email-code requests, Gmail reads, TOTP submissions, phone
branches, and SMS requests.

Only if the context is genuinely unauthenticated:

1. choose Slack **Sign in with email** for `admin@dittto.ai`;
2. read exactly one new Slack login message through profile-local Gmail;
3. submit the authenticator-app TOTP stored in the `sellable-admin` profile;
4. persist the repaired Browserbase context.

Phone, SMS, iMessage, backup codes, copied codes, user checkpoints, and repeated
resend loops are implementation defects. Never screenshot or dump DOM/storage
from email, TOTP, OAuth, or Slack token screens.

## Channel and app identity

- Customer channel names are `sellable-<customerSlug>`.
- App and bot display names are both `Sellable Agent - <customerSlug>`.
- Persist the resolved channel id and app id immediately after creation.
- Resume always adopts those exact IDs; never search by mutable name after an
  ID is known.
- Exact team/workspace mismatch is terminal until a new approved plan.

## Safe manifest

Generate the source manifest with Hermes, then deterministically remove slash
commands, `commands`, App Home/assistant, DM/MPIM events, and IM/MPIM scopes.
Keep only native channel/group mention/history/read, chat/file operations,
minimal public-channel join, Socket Mode, and one app-level
`connections:write` scope.

Compare Slack's normalized manifest on the complete safety projection. When
scope changes require reauthorization, reinstall before accepting the receipt.

## Runtime token capture

Capture `xoxb` and minimal `xapp` only from their exact labeled Slack controls.
When Slack renders a copy-only control, grant clipboard permission only to
`api.slack.com`, validate token shape in memory, clear the clipboard
immediately, and write the pair to the mode-0600 run staging file. Never log,
return, screenshot, or pass tokens in argv.

## Profile and gateway

- Customer profiles are blank builds; never clone `sellable-admin`.
- Use exactly one root `openai-codex` OAuth credential and
  `gpt-5.6-sol`; customer-local auth entries must be zero.
- Install plain Sellable MCP with one immutable workspace lock, one visible
  workspace, no sibling inventory, and no Admin MCP.
- Run one Hermes-owned s6 service `hermes-gateway-<profile>` per customer.
- The container entrypoint owns `/opt/data`, `/opt/data/s6/services`, and the
  `/run/service` scanner as `hermes`; stale root-owned rebuild directories must
  be repaired before a profile promotion.

## Live UAT lessons

- Slack's approved Christian account is `U061L9Y48R1`. A stale fixture ID was
  correctly rejected by the gateway and must not return.
- The pinned Admin Slack wrapper authenticates as a bot and therefore cannot
  prove the allowed-human positive mention. Create that message through Slack
  web with the packaged `runtime/scripts/browserbase-cdp-agent.mjs`, the
  persisted `slack` Browserbase context, and direct CDP. A provisioning session
  plus one released live-UAT session on the same persistent context is expected;
  record both fingerprints and release the UAT session after the checks. Never
  weaken bot-author suppression or treat a bot-authored wrapper post as human.
- Positive beta/cobalt mentions must reply in the originating thread and report
  `gpt-5.6-sol / openai-codex`.
- Test wrong-channel delivery by temporarily joining each app to an isolated
  public channel outside `SLACK_ALLOWED_CHANNELS`; both must remain silent.
  Archive the channel and verify `is_member=false` for both apps afterward.
- App user URLs must not expose a DM composer.
- Crash beta's gateway process under s6, require a new PID, unchanged cobalt
  PID, and a live cobalt reply after recovery.
- Run public rebuild failure checkpoints at `before_promotion` and
  `after_gateway_start`; the previous profile and service must be restored.
- Delete cobalt's customer root and rebuild from the external non-secret plus
  mode-0600 secret bundles and exact package candidates.

## Deterministic receipts

Trust persisted state and redacted receipts, not terminal silence. Every receipt
binds the run id, exact app/team/channel/workspace/profile identities, manifest
hash, package hashes, token fingerprints, file modes, model/auth source, MCP
lock, gateway service/PID/owner count, and Browserbase context fingerprint.
