# Working on Chickpea ## Product UI requires product intent - Do not add diagnostic, debugging, QA, or verification details to customer-facing UI without first asking the maintainer and receiving explicit approval for that UI change. This includes collapsed or Advanced sections, internal IDs, revisions, lifecycle internals, membership IDs, receipts, traces, and raw proposal or delivery records. Hiding them behind a disclosure is not approval. - Requests to fix bugs, add tests, improve observability, verify behavior, or release software do not authorize diagnostic UI. Keep operator evidence in existing authenticated APIs, logs, CLI tools, and private verification artifacts. - Show only information that helps a user complete the requested product task. Useful connection status, actionable errors, and user-facing controls are distinct from internal debugging records. If the need is ambiguous, ask before adding it. Do not invent a diagnostics page or support panel as a workaround. - Tests and verification skills must adapt to this boundary; never add product UI solely to make an automated assertion or manual QA step possible. ## Hosted conversation copies - Copies kept by Chickpea Cloud's "Help improve Chickpea" setting exist so the Chickpea team can read real conversations and improve Chickpea. When a hosted bug or behavior question involves a workspace with the setting on, read its copies by the Cloud repo's runbook R26: `npm run ops -- review list|get`, each with `--target` and a real `--reason`; `get` also takes `--key` and `--out`. AI tools may read them, including Claude and Codex sessions on personal plans, when the account's terms or settings keep the provider from training on them (training off, or zero data retention). - Copies are never used to train AI models, and never sold or shared for anyone else's use. - No copy content, verbatim or lightly edited, goes into the repository, tests, fixtures, evals, docs, marketing, issue trackers, or shared chat channels. Discussing it with the maintainer in the working session is fine. Rewrite a reproduction with synthetic content that keeps only the structure (message shape, tool sequence, error class). Counts, error classes, and timings may leave the store. Keep decrypted copies under `~/.chickpea/hosted-ops/` and delete them when the investigation ends. ## Development and delivery - Use a topic branch and a GitHub pull request by default. A maintainer may explicitly authorize a verified local worktree merge into root `main` and a direct push. Do not force-push `main`. - Run tests and release verification locally. Run `npm run verify:hygiene` (seconds) before every merge; `npm run hooks:install` makes the pre-push hook run it per pushed commit. Do not add GitHub Actions workflows or require GitHub-hosted test results for merging. The one exception is `.github/workflows/publish-cli.yml`, which runs only on a `cli-v*` tag to publish `packages/cli` to npm; it is not a merge gate and never runs on push. - Do not squash existing repository history, create release tags, or publish a release without an explicit request. A request to implement a change does not authorize a production deployment. - Change stored meaning, bump the contract: a config store change to what it writes or how it reads stored data raises that entry's version in `src/config/storage-contract.json`. - Always roll forward; nothing rehearses or gates on a rollback. Migrations only add: a table, an index, or a nullable or constant-default column. Ship a drop, rename, type change, data rewrite, or new NOT NULL as expand, then contract in a later release. New code reads old data. `verify:hygiene` checks the migrations; see [Roll forward](docs/runbooks/releasing.md#roll-forward). - Preserve unrelated changes. Keep private environment bindings, credentials, live transcripts, and working documents out of the public repository. - Follow [CONTRIBUTING.md](CONTRIBUTING.md). Run tests and builds serially in each checkout. Use isolated databases for tests; never reset an operator's state. - For development and QA, run expensive full-suite/build/workerd groups through `verify:regression` or `verify:host`, which allow two at a time on this host; see [host coordination](qa/live/operator/host-checks.md). This includes deployment builds during development and QA. Customer installs and updates from published releases, with only local installation settings changed, follow the [install guide](INSTALL_CHICKPEA_CLOUDFLARE.md) or [update guide](UPDATE_CHICKPEA_CLOUDFLARE.md) without contributor host reservations, process inspection, or test suites. Continue lightweight independent checks; never stop another task's processes. - For Cloudflare deployment use `npm run deploy` (the guarded wrapper), not bare `wrangler deploy`. Confirm the intended target before any live mutation. - For ordinary customer updates, follow the [update guide](UPDATE_CHICKPEA_CLOUDFLARE.md) and verify the serving version and signed-in Admin. Do not send Slack messages or invoke Agents, connections, or schedules unless the user requests those checks. Maintainer development and release QA follow their separately authorized checks. - Report local checks and live acceptance separately. A source SHA, build, or deployment upload is not evidence of a real Slack reply. - Before changing Slack senders, avatars, attachments, streaming, message updates, or attribution footers, read [Slack message identity](docs/runbooks/slack-message-identity.md). Verify sender, file owner, footer, and recipient-visible attachment separately; compare exact-message API readback with fresh desktop and real mobile views. - For repeated implementation/retest cycles against real Slack, start with an exclusive local Cloudflare Worker lane from [local Worker development](docs/runbooks/local-worker-development.md). Run `npm run dev:cf -- status --lane ` before acting; it must identify the workerd runtime, HTTP Slack transport, immutable app/workspace pair, provider model, persistent state, public endpoint, and owning worktree. Local passes do not satisfy shared-gateway or deployed Amber/Cobalt acceptance. - For Chickpea live Slack verification, invoke `$chickpea-live-verification` (Codex) or `/chickpea-live-verification` (Claude Code). Both shims, `.agents/skills/chickpea-live-verification/SKILL.md` and `.claude/skills/chickpea-live-verification/SKILL.md`, point at `qa/live/operator/SKILL.md`. Once per merge batch on `main`, after the batch merges and before any Chickpea Cloud pin or Core release, run the scripted [batch smoke](qa/live/operator/smoke.md), `npm run verify:smoke`. Per PR, run only the live check the [risk class](#shipping-by-risk-class) names: the T2 probe (the skill's `probe` mode), the T3 update or rehearsal, the T4 money proof, or the T0x dry run. Run it from the implementation commit in parallel with review, and redeploy only when a fix round touches runtime code. A comment-only or docs-only commit never repeats a lane check. A request to run it authorizes its declared QA actions and exact cleanup, including test messages, product approvals, and OAuth with registered test accounts. Do not request the same permission at every step. Use its mode and authorization boundaries; production/shared infrastructure are outside scope. If skill discovery is unavailable, read the entrypoint `.claude/skills/chickpea-live-verification/SKILL.md` (it names the profile), then `qa/live/operator/SKILL.md`, before acting. It points to the public catalog and runbook. Keep target coordinates, resolved aliases, journals, transcripts, screenshots, and evidence outside this repository. - For runtime failures, unexplained behavior, or timing issues, consult existing evidence and [runtime observability](docs/runbooks/runtime-observability.md). Use `npm run diagnose -- --help` for a bounded request-to-trace lookup and preserve the first failure's private evidence before changing state or retrying. Use the owning terminal and Local Explorer for a local Worker. Diagnose on the deployed Worker when the question depends on its serving version, bindings, gateway route, credentials, Cloudflare telemetry, or due-time scheduling. Use Cloudflare API MCP (`cloudflare-api`) `search` then `execute` for bounded historical logs, invocations, traces, aggregate metrics, and read-only infrastructure inspection; prefer `dry: true` queries. Use Wrangler `tail` for authorized live reproductions, attached before the action and stopped afterward. Use the dashboard for visual traces or access fallback. - Resolve the account, Worker, environment, and serving version before querying. MCP, Wrangler, and dashboard access are independent. Missing telemetry is not proof of product failure; Slack/Admin/provider readback remains acceptance. Keep logs and incident artifacts private. Use existing repository commands for local development/builds and the guarded wrapper for deployments. ## Shipping by risk class The author declares the risk class in the PR description, and the reviewer confirms it. The class decides what blocks merge and what blocks production, which here means Chickpea Cloud running a pinned Core commit. | Class | Examples | Before merge | Before production | | --- | --- | --- | --- | | T0 docs and workflow | runbooks, README, `qa/` prose, package file lists | `verify:hygiene`, plus review only for prose with product, process, or legal weight | nothing | | T0x executable guides | install, update, and uninstall guides an agent follows | review, plus one live dry run on a throwaway target in parallel | nothing | | T1 host-only | refactors, tests, scripts, CLI tooling, logic fully covered by unit tests | the per-PR checks below, review, the automatic security review, and the train's full regression | Cloud's staging attestation, standard promotion, and canary | | T2 live-surface behavior | routing, tools, Admin behavior, models and providers, connections, Slack features | T1, plus a probe only when the change rests on an outside fact no test reaches | a passed batch smoke on the Core commit Cloud pins | | T3 storage, contract, boundary, dependency | migrations, `release.json` digests, storage generation, secrets, auth boundaries, Flue or Pi upgrades, installer, deploy scripts, sandbox image | a short design note first, review plus a security-focused delta review, the train's full regression, and the live check below | a passed batch smoke and Cloud's full promotion | | T4 product shape, privacy, money | new data handling, billing, policy, pricing | research and an adversarial design review before code, then T2 or T3 as the code warrants, plus one staging end-to-end proof for money flows | as T2 or T3 | - A probe checks a named assumption such as a Slack field, a provider parameter, or an OAuth flow. It is 1 to 3 cases and takes 15 minutes or less including deploy. Skip it when the author and reviewer agree the change rests on no such fact. - The T3 live check depends on the change. Storage changes get an in-place update of a lane that holds existing data. Installer or deploy changes get a rehearsal with the real artifact. Provider or dependency upgrades get a diff of captured request bodies. - Each PR runs `npm run test:mcp` when it touches the management MCP program, and the tests related to anything else it changes, plus `npm run typecheck`, `npm run verify:hygiene`, and the budgets: `npm run verify:host -- --wait-ms 600000 npm run verify:budgets`. T3 live checks stay per PR. - PRs merge in trains. A train is the merge batch the batch smoke runs on: one or more PRs, and a PR that ships alone, such as a hotfix, is a train of one. The coordinator of the batch builds it in its own worktree: `git fetch origin`, then each PR merged with `--no-ff` onto current `origin/main`, in order. The train's merged tree is the final tree for its one full regression, batch smoke, and staging pin. When the regression passes, merge the train's PRs on GitHub in the same order with `gh pr merge --merge --match-head-commit`, then confirm `origin/main`'s tree equals the tested tree (`git rev-parse origin/main^{tree}`). If `origin/main` moved first, or the trees differ, rebuild the train and repeat the regression. - When a train's regression fails, find the first merge that fails: rerun the failing test files at each merge, or `git bisect start --first-parent HEAD origin/main`. Fix that PR on its branch or drop it, rebuild the train from that merge on, and repeat the regression. A failure that needs two PRs together is fixed in the later one. - A full regression is `npm run verify:host -- --wait-ms 3600000 npm run verify:train`: a build, both startup budgets for each merge, then the full suite. Run it on the train's merged tree, because two PRs can each pass alone and fail together. A commit added to the train afterwards repeats it, unless it changes only comments or docs. Cloud pins a train's tip, never a merge inside it. A `verify:regression` plan with `fullTests: false` runs a subset and is not this gate; the tool says so. - Review means a fresh, independent first pass (on Claude, a Fable subagent; on Codex, an equivalent adversarial review). Start it when the PR opens, in parallel with gates and probes. The reviewer may apply small, local fixes itself. - Run a delta re-review only for security, storage, or money fixes, or a fix larger than about 50 changed lines. The automatic commit security review always runs. - A PR gets at most one first review. The security-focused delta review belongs to T3 and T4 only; T1 and T2 rely on the automatic commit security review. - A PR gets at most three fix rounds. A fix round is the author's answer to one batch of review findings, however many pushes it takes. The reviewer's own small fixes, a fix for a failed gate, and a train's rebuild are not fix rounds. After the third, never start a fourth. Redesign instead, in a design note and a new PR, or write each remaining finding into the PR description as accepted residual risk with its reason, then merge or close. A remaining security, privacy, or money finding is the maintainer's to accept: ask once, and hold only that merge. Findings that keep naming new cases of one kind mean the approach is wrong, not that it needs another round. - Run a mutation pass at most once per PR, by the author or the reviewer, not both. A fix round tests its change with ordinary tests. A long harness such as `npm run mcp-cards:harness` runs at most once per PR and once per train, not once per reviewer. ## Human gates - Do not ask the maintainer to approve wording. Choose the words for UI strings, error text, docs, marketing, legal, and pricing copy. List new or changed customer-visible strings in the PR description and flag legal and pricing text. The maintainer may change them after merge. - Ask once, up front. When you first clarify the task, send one batched question with the product calls and the authorizations the thread will need, such as a production deploy once verified, a Core release after, and any spending. - Never ask whether to continue into work the original request covered, whether to merge a PR that passed its class's checks, or whether to start the next independent unit. - When a question must wait for its answer, keep running everything that does not depend on it. Gate only the irreversible step. - A production deploy needs a grant given in the thread. Spending and paid resources, resource or data deletion, customer messages, and publishing a Core release tag also need the maintainer. A grant given in the thread covers the tag.