# controlplane9 — Agent Guidance

Meta rules for working in this repo. For project / design context, ask the user which doc to read; do not pre-load docs unprompted.

## Agent skills

### Issue tracker

Issues and PRDs are tracked in GitHub Issues for `tidbcloud/controlplane9`. See `docs/agents/issue-tracker.md`.

### Triage labels

Use the default five-role triage label vocabulary. See `docs/agents/triage-labels.md`.

### Domain docs

Use a single-context domain-doc layout; proceed silently when `CONTEXT.md` or `docs/adr/` is absent. See `docs/agents/domain.md`.

Design docs live under `docs/`. Treat them as read-only for project context; do not pre-load them. Read a specific doc only when the user asks you to or when the current task explicitly references one.

## Build & Test

CP9 product spec and functional QA instructions live in
[`spec-tests/README.md`](spec-tests/README.md).

```bash
make build            # Build binary to bin/cp9
make test             # Fast unit tests
make test-race        # Unit tests with race detection
make test-coverage    # Unit tests with coverage profile
make test-integration # TestIntegration_*; k3d/AWS/live prereqs skip with explicit reasons
make lint             # golangci-lint v2
make fmt              # Auto-format (gci + gofmt + golines)
make dev              # Full cycle: generate + mod + fmt + build + test
```

## Pre-push Checklist (MANDATORY)

Before pushing any branch or creating/updating a PR, you MUST run and pass:

```bash
make fmt          # Auto-format first
make lint         # Must pass with zero issues
make test         # Must pass with zero failures
```

Do NOT push code that has not passed `make lint` locally. CI will reject it.

**Important**: Before pushing any branch or creating/updating a PR, you MUST first remove newly added unit or integration tests, then pass ponytail skill review. This repo only needs product spec and functional QA instructions in [`spec-tests/README.md`](spec-tests/README.md).

### Runtime and Non-Runtime-Check Boundary (MANDATORY)

- Functionality whose only purpose is enforcing a constraint that can be fully
  verified at build, test or review time **MUST NOT** be added to a runtime code path.
- Feature-specific or custom non-runtime checks may be written during local
  implementation, but they **MUST** be removed before pushing a branch or
  creating/updating a PR.
- A feature-specific or custom non-runtime check may be retained only when it is
  critical and has substantial long-term value. Retained checks **MUST** remain
  separate from runtime code, live under the repository-root `tools/`
  directory, and include rich documentation describing their use cases and the
  problems they solve.
- Only logic that must execute at runtime to satisfy real product,
  operational, or safety requirements may be added to `cp9` command runtime
  code paths.

## Control-Format Priority (MANDATORY)

**One fact, one place.** The same value in two formats will diverge, and then both
are suspect. Duplication is the defect; the priority below decides where the one
copy goes.

**Use the least powerful format that can hold it.** For configuration the order is
Kustomize manifest first, then shell or python, then Go. A weaker format bounds what
the code can be doing, so a reader can stop early. Go can do anything, so Go tells
you the least per line.

**Tilt owns the dev loop, not configuration.** `live_update`, `resource_deps`, build
triggers and port-forwards belong to Tilt. A value that lands in a rendered manifest
does not. Tiltfiles are Starlark, so configuration there gains none of a manifest's
constraint.

**Descend only when the higher format cannot express the thing, and descend with a
value, not a document.** Minting STS or ECR credentials, apply ordering (Kustomize
has no ordering primitive), readiness gating and patching dataplane clusters that do
not exist at render time are legitimately imperative. None of that justifies code
owning a whole manifest.

**Prefer the native mechanism to a hand-rolled one:** `replacements:` over string
substitution, `configMapGenerator` over a hand-written ConfigMap, `images:` over
rewriting an image reference, an overlay with `$patch: delete` over filtering
rendered YAML, and the generator name hash over a hand-computed rollout annotation.
Disabling a native mechanism forces a reimplementation downstream.

**A render change is a reviewable event.** A refactor that preserves behaviour must
leave `tools/render-baseline.sh check` clean; say so in the PR. If the render does
change, that diff is the change.

## Namespace Prefix Isolation

Every external resource cp9 creates — k3d clusters, TiDB Cloud instances, AWS resources (S3 buckets, SQS queues, DynamoDB tables, etc.) — **SHOULD** be named with the top-level `name` field from `cp9.yaml` as a prefix. This isolates concurrent cp9 environments.

- The `name` field is DNS-compatible (lowercase alphanumeric + hyphens, 3–32
  chars).

When adding a new external resource type, accept the namespace prefix and use it in the resource name. Never hardcode `cp9-dev` or similar — derive from `cfg.Name`.

## Tag Invariant

Every cloud resource cp9 (or any service running inside it) creates **SHOULD** carry both:

- `cp9-owner` — developer's AWS SSO email (or `CP9_OWNER` for CI).
- `cp9-session` — the session `name`.

Untagged creation paths are invisible to `cp9 destroy` Stage 2 (the tag-scoped reaper) and become resource leaks. Adding a new code path that creates cloud resources without these tags is a bug, regardless of whether the relevant test passes.

## Asynchronous Destroy Handoff (MANDATORY)

After `cp9 destroy` reports that an exact cleanup workflow dispatch was
accepted for the intended owner, session, account, and region, do **not** block
the main task waiting for that GitHub Actions run to complete. Record the
handoff details and continue immediately with independent work such as a
rebase, build, tests, or a fresh uniquely named session.

## AWS Credentials (MANDATORY)

All CP9 workload — tests, proof, resource tracking, IAM validation, etc. — **MUST** use the shared `cp9-developer` IAM role unless the user explicitly provides temporary credentials.

Setup and role-management details live with the AWS bootstrap/Terraform roots:
- Daily cp9 user profile setup: `terraform/aws/cp9-substrate/README.md#local-cp9-aws-profile`
- Admin `cp9-developer` role management: `terraform/cp9-developer/SKILL.md`

Rules:
- Default to `cp9-developer` for all AWS work (tests, resource creation, cleanup).
- Only use other credentials when the user explicitly overrides with temporary credentials.
- Never hardcode or commit AWS credentials.

## Shared CP9 Dependencies (MANDATORY)

CP9 runtime accounts depend on three protected shared boundaries. They are
account-level infrastructure, never session resources, and `cp9 destroy` must
never delete or narrow them:

- **Auth0:** tenant `tidb-dev.us.auth0.com`, application
  `KiwWajLJ3pto8SsZ7F35NaqNhwnoRh1c`, and post-login Action `AddOrgInfo`
  (`b1749b03-cb92-4d15-88af-243ffbe10c3f`). Each runtime account requires the
  exact callback/logout/origin wildcard for
  `https://*.<account-id>.cp9.dev.aws.tidbcloud.com`; the Action must call the
  requesting session origin's `/action-verify`. Preserve other consumers'
  fallback behavior. Account initialization requires an Auth0 administrator,
  and any Auth0 change requires a real CP9 browser login through the callback
  to the authenticated shell.
- **Account-414 DNS:** AWS account `385595570414`, public hosted zone
  `dev.aws.tidbcloud.com` (`Z07443793I93RI8K8Z4F7`) owns one NS delegation per
  CP9 runtime account. Each runtime account owns
  `<account-id>.cp9.dev.aws.tidbcloud.com`, its records, and its wildcard ACM
  certificate. New sessions write only that local child zone; account414 is an
  account-bootstrap dependency, never a session writer. Record the exact child
  zone ID/name and certificate ARN in `.github/cp9-cleanup-accounts.json`.
  Keep the legacy `cp9-source-route53-records` trust until every old direct-zone
  session has been destroyed; it is not authority for new sessions. Do not
  delete the old one-label wildcard certificate or validation record while an
  old session may still reference it.
- **Account-414 ECR:** AWS account `385595570414` owns the immutable
  `tidbcloud/cp9-dbaas-ui` repository and the existing Dev Full source-image
  repositories. Preserve the complete runtime-account pull/publish policy sets
  in `terraform/cp9-developer/ecr-merge.md`. A partial runtime-account
  grant revokes every omitted account.

Before changing any boundary above, inspect its live consumers and Terraform
state. After changing it, prove the affected cross-account operation against a
real runtime account; configuration review alone is insufficient.

## Real Staging Fidelity

- Global CP: arn:aws:eks:us-west-2:385595570414:cluster/staging-base-us-west-2
- Premium regional CP: arn:aws:eks:us-west-2:385595570414:cluster/staging-seed-us-west-2-ng-0711
- Starter regional CP+DP: arn:aws:eks:us-east-1:385595570414:cluster/staging-us-east-1-f01
- Dedicated regional CP: arn:aws:eks:us-west-2:385595570414:cluster/staging-seed-us-west-2
- Premium and Dedicated DPs are managed by their regional CP in each region. And their DPs is create/delete dynamiclly regarding API call. Not provisioned in CD stage.
- O11Y staging global and region control planes: arn:aws:eks:us-west-2:552185537300:cluster/o11y-staging-global-us-west-2, arn:aws:eks:us-west-2:552185537300:cluster/o11y-staging-regional-us-west-2.

find evidence from real staging via awscli or kubectl before any concrete solution, do not guess or infer quickly 

## PR Workflow

When addressing PR review comments:
1. Think critically before changing code. Reject suggestions confidently if you disagree; reply with the reasoning instead of silently complying.
2. **Resolve the comment threads** after addressing them — `gh api graphql` with the `resolveReviewThread` mutation.
3. Push the fixes and confirm all threads are resolved before requesting re-review.

When merging, must use squash merge.
