Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -397,10 +397,10 @@ Implemented command behavior:
- `tdc organization list-projects`
- `tdc organization list-projects --query 'projects[0].id'`
- `tdc organization list-projects --output text`
- `tdc db create-db-cluster --db-cluster-name demo --db-cluster-type starter`
- `tdc db create-db-cluster --db-cluster-name demo --db-cluster-type starter --wait`
- `tdc db create-db-cluster --db-cluster-name demo --db-cluster-type starter --dry-run`
- `tdc db create-db-cluster --db-cluster-name demo --db-cluster-type starter --project-id <project-id>`
- `tdc db create-db-cluster --db-cluster-name demo`
- `tdc db create-db-cluster --db-cluster-name demo --wait`
- `tdc db create-db-cluster --db-cluster-name demo --dry-run`
- `tdc db create-db-cluster --db-cluster-name demo --project-id <project-id>`
- `tdc db list-db-clusters`
- `tdc db list-db-clusters --query 'clusters[].id'`
- `tdc db describe-db-cluster --db-cluster-id <cluster-id>`
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ An agent can go from zero to live HTAP SQL (Hybrid Transaction / Analytical Proc
1. Provision a serverless MySQL-compatible cluster, wait until it is active, and capture its ID

```shell
export CLUSTER_ID="$(tdc db create-db-cluster --db-cluster-type starter --db-cluster-name my-app-db --wait --query id --output text)"
export CLUSTER_ID="$(tdc db create-db-cluster --db-cluster-name my-app-db --wait --query id --output text)"
```

2. Create the SQL users it needs to connect
Expand Down Expand Up @@ -145,7 +145,7 @@ tdc fs mount-file-system --file-system-name agent-workspace --mount-path /path_t
### TiDB Cloud Starter

```shell
tdc db create-db-cluster --db-cluster-name my-distributed-mysql --db-cluster-type starter --wait
tdc db create-db-cluster --db-cluster-name my-distributed-mysql --wait
```

Cluster creation uses the configured `project_id` by default. Use optional `--project-id <project-id>` to create in another accessible project. An explicit empty `--project-id` is rejected instead of falling back to the profile.
Expand Down
2 changes: 1 addition & 1 deletion docs/pingcap-docs/docs
Submodule docs updated 155 files
22 changes: 12 additions & 10 deletions docs/spec/done/0006-starter-db-cluster-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ Initial command set:
Primary create shape:

```bash
tdc db create-db-cluster --db-cluster-name <name> --db-cluster-type starter
tdc db create-db-cluster --db-cluster-name <name> --db-cluster-type starter --wait
tdc db create-db-cluster --db-cluster-name <name>
tdc db create-db-cluster --db-cluster-name <name> --wait
```

## Behavior

- `tdc db` manages TiDB Cloud Starter database clusters.
- Require `--db-cluster-type starter` for create to preserve future tier
compatibility.
- Default `--db-cluster-type` to `starter`. The optional flag remains available
for an explicit declaration and rejects unsupported values.
- Use long flags only.
- Mutating commands support `--dry-run`.
- Commands must not prompt.
Expand Down Expand Up @@ -81,10 +81,10 @@ Users can create and manage Starter clusters in the configured cloud provider
and region:

```bash
tdc db create-db-cluster --db-cluster-name demo --db-cluster-type starter --dry-run
tdc db create-db-cluster --db-cluster-name demo --db-cluster-type starter
tdc db create-db-cluster --db-cluster-name demo --db-cluster-type starter --wait
tdc db create-db-cluster --db-cluster-name demo --db-cluster-type starter --project-id <project-id>
tdc db create-db-cluster --db-cluster-name demo --dry-run
tdc db create-db-cluster --db-cluster-name demo
tdc db create-db-cluster --db-cluster-name demo --wait
tdc db create-db-cluster --db-cluster-name demo --project-id <project-id>
tdc db list-db-clusters --query 'clusters[].id'
tdc db describe-db-cluster --db-cluster-id <id>
tdc db delete-db-cluster --db-cluster-id <id>
Expand Down Expand Up @@ -124,7 +124,8 @@ Command mapping:
`pageToken`, `orderBy`, and `skip`.
3. Filter or validate Starter-only behavior using returned cluster metadata.
- `tdc db create-db-cluster`
1. Validate `--db-cluster-type starter`.
1. Default an omitted `--db-cluster-type` to `starter` and reject any other
explicit value.
2. Resolve explicit `--project-id` or the profile's configured `project_id`
and set label `tidb.cloud/project`.
3. Call `POST /v1beta1/clusters` with `displayName`, project label, region
Expand Down Expand Up @@ -178,7 +179,8 @@ Available but not part of this lifecycle MVP:
terminal state, polling failure, timeout, and default non-wait behavior.
- Delete wait tests cover `DELETING` to `DELETED`, inaccessible-after-delete,
timeout, and dry-run wait-plan reporting.
- Tests cover required `--db-cluster-type starter` on create.
- Tests cover the default `starter` type and rejection of unsupported explicit
cluster types.
- Tests cover dry-run request validation and wait-plan reporting without
sending mutating requests.
- Tests cover stable JSON output and `--query`.
Expand Down
60 changes: 19 additions & 41 deletions docs/spec/done/0019-pingcap-tdc-documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,25 @@ The English repository uses these relative paths:
ai/tdc/
tdc-overview.md
tdc-quick-start.md
concepts/
tdc-concepts-and-architecture.md
guides/
reference/
tdc-cli-reference.md
tdc-install-configure-update.md
tdc-organization.md
tdc-starter-database.md
tdc-filesystem.md
tdc-filesystem-git.md
tdc-filesystem-journal.md
tdc-filesystem-vault.md
examples/
tdc-agent-sandbox-example.md
tdc-daily-workflow-example.md
tdc-query-sql-with-roles-example.md
tdc-share-filesystem-across-machines-example.md
tdc-git-workspace-for-agents-example.md
tdc-journal-agent-workflow-example.md
tdc-vault-agent-secrets-example.md
reference/
tdc-cli-reference.md
tdc-ci-artifact-handoff-example.md
tdc-persistent-agent-state-example.md
tdc-parallel-agent-dataset-example.md
tdc-configuration-and-credentials.md
tdc-regions-security-and-limitations.md
tdc-troubleshooting.md
Expand All @@ -76,7 +75,7 @@ Follow the existing PingCAP documentation templates and AI documentation style:
- Every page has YAML front matter with an SEO-oriented `title` and `summary`.
- The front matter title and the page H1 are identical.
- Task pages use a short introduction, prerequisites, numbered steps, expected results, cleanup where resources are created, and a "What's next" section.
- Concept and reference pages use the corresponding PingCAP concept/reference templates.
- Reference pages use the corresponding PingCAP reference templates.
- Notes and warnings use the standard blockquote format used by the PingCAP docs repositories.
- Prose is concise and task-oriented. Do not add manual line breaks inside ordinary paragraphs without a semantic reason.
- Commands use the installed `tdc` name, never the local development path `bin/tdc`.
Expand Down Expand Up @@ -126,33 +125,9 @@ The Filesystem quick path uses data-plane commands to write and read one file. I

The database quick path creates or selects a Starter cluster, prepares SQL users when needed, and executes a small verification query with an explicit role. Keep resource-ID extraction and cleanup understandable; do not require users to understand every output/query option before their first successful operation.

## Concepts

`tdc-concepts-and-architecture.md` explains only concepts needed to understand later guides:

- tdc as the TiDB Cloud CLI for Starter databases and TiDB Cloud Filesystem;
- the two-level command model and agent-friendly deterministic behavior;
- profile namespace, canonical region code, and default virtual project;
- one profile to many Filesystem resources;
- TiDB Cloud API keys versus generated DB SQL credentials;
- read-only, read-write, and admin SQL roles;
- FS owner token versus delegated vault token;
- local config, credentials, resource registry, DB user credentials, mount state, and operation logs;
- tdc and Drive9 companion responsibilities.

tdc bundles and invokes the Drive9 companion as `tdc-drive9`. tdc owns profile selection, credential resolution, region routing, resource selection, output, and error behavior. Drive9 owns Filesystem data-plane semantics, FUSE/WebDAV mount runtime, layers, pack/unpack, Git workspace, journal, and vault behavior.

Link the Drive9 name to its GitHub repository:

```text
https://github.com/mem9-ai/drive9
```

Users must not be instructed to install, configure, authenticate, or invoke standalone Drive9 for normal tdc workflows.

## Guides
## Command References

The guides cover every implemented command, grouped by the existing top-level command families.
The command references cover every implemented command, grouped by the existing top-level command families. Each family page includes a tree of its commands and documents every command with its purpose, key inputs, and one or two valid examples.

### Install, configure, and update tdc

Expand Down Expand Up @@ -221,9 +196,9 @@ The guide must also provide a concise platform matrix for macOS, Linux, and Wind

`tdc-filesystem-vault.md` covers secret create, replace, read, list, and delete; delegated grants; grant revocation; audit events; environment injection; read-only vault mount; and unmount. It distinguishes owner FS credentials from delegated vault tokens and must not print a token in an example output.

## Examples
## Scenario References

Examples are end-to-end scenarios rather than command catalogs. Every example includes prerequisites, commands, expected verification, security notes, and cleanup.
Scenario references are end-to-end workflows rather than command catalogs. Every scenario includes prerequisites, commands, expected verification, security notes, and cleanup.

- `tdc-agent-sandbox-example.md`: provision on a trusted machine, pass `TDC_FS_TOKEN`, `TDC_REGION_CODE`, and `TDC_FS_FILE_SYSTEM_NAME` to a clean sandbox, then use FS, mount, Git, journal, or vault without TiDB Cloud API keys.
- `tdc-daily-workflow-example.md`: install, configure, inspect projects, manage one Starter cluster and one Filesystem resource, update tdc, and clean up.
Expand All @@ -232,12 +207,15 @@ Examples are end-to-end scenarios rather than command catalogs. Every example in
- `tdc-git-workspace-for-agents-example.md`: mount a Filesystem, clone or hydrate a repository, create a worktree, use ordinary Git, and remove the worktree safely.
- `tdc-journal-agent-workflow-example.md`: create a journal, append agent events, search them, and verify the hash chain.
- `tdc-vault-agent-secrets-example.md`: create a secret, delegate a limited field to an agent, inject it into a process, inspect audit events, revoke access, and clean up.
- `tdc-ci-artifact-handoff-example.md`: persist build output under a run-specific path and consume it from another isolated CI job.
- `tdc-persistent-agent-state-example.md`: preserve task plans, checkpoints, results, and journal history across disposable sandboxes.
- `tdc-parallel-agent-dataset-example.md`: expose one shared unstructured dataset through read-only mounts to parallel agent workers.

Examples that mount on macOS default to WebDAV unless the example specifically demonstrates the optional macFUSE/FUSE path.

## Reference
## Supporting Reference

`tdc-cli-reference.md` documents global flags, long-flag rules, required-before-optional help ordering, JSON/text output, JMESPath queries, dry-run behavior, help/version forms, stable error prefix, exit behavior, and FS alias mapping.
`tdc-cli-reference.md` documents the complete command tree, global flags, long-flag rules, required-before-optional help ordering, JSON/text output, JMESPath queries, dry-run behavior, help/version forms, stable error prefix, exit behavior, and FS alias mapping.

`tdc-configuration-and-credentials.md` documents:

Expand Down Expand Up @@ -265,7 +243,7 @@ Update:
docs/pingcap-docs/docs/TOC-ai.md
```

Under Quick Start, Concepts, Guides, Examples, and Reference, add a nested `TiDB Cloud CLI (tdc) (Preview)` group with direct links to all corresponding tdc pages. Do not expose only the overview while leaving the other pages unreachable from the TOC.
Keep tdc Quick Start under Quick Start. Put the overview, every command-family reference, every scenario reference, configuration, regions, security, limitations, and troubleshooting under a nested `TiDB Cloud CLI (tdc)` group in Reference. Do not add tdc pages under Concepts, Guides, or Examples.

Update the AI home page:

Expand Down Expand Up @@ -345,11 +323,11 @@ No example may depend on an undocumented endpoint, raw server URL, or direct Dri
- The overview links feedback to `https://github.com/tidbcloud/tdc/issues`.
- Drive9 references link to `https://github.com/mem9-ai/drive9` and explain the bundled companion boundary.
- Quick Start reaches a successful DB or FS operation without introducing advanced features.
- Guides cover every current implemented command and every FS alias.
- No guide includes `tdc fs-git restore-git-workspace` or another absent command.
- Command references cover every current implemented command and every FS alias, with command trees and examples.
- No reference includes `tdc fs-git restore-git-workspace` or another absent command.
- No published page documents source compilation as an installation method.
- FS documentation states that macOS defaults to WebDAV and explains how installing macFUSE and explicitly selecting FUSE enables the complete mount experience.
- Examples use only synthetic values and never expose a real secret.
- Scenario references use only synthetic values and never expose a real secret.
- `docs/priciples.md`, `docs/present.md`, README, AGENTS, and the listed completed specs no longer present superseded behavior as current behavior.
- Archived README snapshots and historical release notes remain unchanged.
- English Markdown lint and link checks pass using the documentation repository's available validation workflow.
Expand Down
4 changes: 2 additions & 2 deletions e2e/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ func TestHelpAndVersion(t *testing.T) {
createDBCluster := runTDC(t, bin, "db", "create-db-cluster", "help")
createDBCluster.wantExitCode(0)
createDBCluster.wantStdoutContains("--db-cluster-name <string> (required)")
createDBCluster.wantStdoutContains("--db-cluster-type <string> (required)")
createDBCluster.wantStdoutContains("[--db-cluster-type <string>]")
createDBCluster.wantStdoutNotContains("--db-cluster-type <string> (required)")
createDBCluster.wantStdoutContains("--project-id <string>")
createDBCluster.wantStdoutNotContains("--project-id <string> (required)")

Expand Down Expand Up @@ -232,7 +233,6 @@ func createClusterDryRunArgs() []string {
return []string{
"db", "create-db-cluster",
"--db-cluster-name", "demo-cluster",
"--db-cluster-type", "starter",
"--project-id", "project-1",
"--wait",
"--dry-run",
Expand Down
1 change: 0 additions & 1 deletion e2e/live_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1464,7 +1464,6 @@ func TestLiveDBClusterLifecycle(t *testing.T) {
"--profile", profileName,
"db", "create-db-cluster",
"--db-cluster-name", clusterName,
"--db-cluster-type", "starter",
"--wait",
)
create.wantExitCode(0)
Expand Down
4 changes: 2 additions & 2 deletions internal/cli/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,11 +209,11 @@ func newDBCreateClusterCommand(info version.Info) *cobra.Command {
},
}, info)
cmd.Flags().String("db-cluster-name", "", "Starter DB cluster display name")
cmd.Flags().String("db-cluster-type", "", "DB cluster type; must be starter")
cmd.Flags().String("db-cluster-type", "starter", "DB cluster type; must be starter")
cmd.Flags().String("project-id", "", "TiDB Cloud project id")
cmd.Flags().Int32("monthly-spending-limit-usd-cents", -1, "monthly spending limit in USD cents; omit to use the API default")
cmd.Flags().Bool("wait", false, "wait until the created cluster becomes ACTIVE before returning")
markUsageRequired(cmd, "db-cluster-name", "db-cluster-type")
markUsageRequired(cmd, "db-cluster-name")
return cmd
}

Expand Down
11 changes: 9 additions & 2 deletions internal/cli/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,8 @@ func TestHelpUsageShowsRequiredFirstAndOptionalBracketed(t *testing.T) {
}
for _, want := range []string{
"--db-cluster-name <string> (required)",
"--db-cluster-type <string> (required)",
"[--db-cluster-type <string>]",
"--db-cluster-type <string>",
"--project-id <string>",
"--monthly-spending-limit-usd-cents <int32>",
} {
Expand All @@ -273,6 +274,12 @@ func TestHelpUsageShowsRequiredFirstAndOptionalBracketed(t *testing.T) {
if strings.Contains(stdout, "--project-id <string> (required)") {
t.Fatalf("optional --project-id must not be marked required, got:\n%s", stdout)
}
if strings.Contains(stdout, "--db-cluster-type <string> (required)") {
t.Fatalf("defaulted --db-cluster-type must not be marked required, got:\n%s", stdout)
}
if !strings.Contains(stdout, `--db-cluster-type <string>`) || !strings.Contains(stdout, `(default "starter")`) {
t.Fatalf("expected --db-cluster-type to show the starter default, got:\n%s", stdout)
}

stdout, _, err = executeForTest("db", "delete-db-cluster", "help")
if err != nil {
Expand Down Expand Up @@ -635,7 +642,7 @@ func TestControlPlaneCommandSpecUsesCustomDryRun(t *testing.T) {
func TestMutatingControlPlaneDryRunRendersJSON(t *testing.T) {
withConfigEnv(t)

stdout, _, err := executeForTest("db", "create-db-cluster", "--db-cluster-name", "demo-cluster", "--db-cluster-type", "starter", "--project-id", "project-1", "--wait", "--dry-run")
stdout, _, err := executeForTest("db", "create-db-cluster", "--db-cluster-name", "demo-cluster", "--project-id", "project-1", "--wait", "--dry-run")
if err != nil {
t.Fatalf("expected dry-run to succeed, got %v", err)
}
Expand Down
31 changes: 25 additions & 6 deletions internal/db/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/tidbcloud/tdc/internal/api/endpoints"
"github.com/tidbcloud/tdc/internal/apperr"
"github.com/tidbcloud/tdc/internal/config"
"github.com/tidbcloud/tdc/internal/db/validate"
)

func TestCreateCluster(t *testing.T) {
Expand Down Expand Up @@ -518,17 +519,35 @@ func TestDryRunDeleteClusterDescribesWait(t *testing.T) {
}
}

func TestCreateRequiresStarterType(t *testing.T) {
_, err := Service{}.DryRunCreateCluster(context.Background(), "tdc db create-db-cluster", CreateClusterOptions{
func TestCreateDefaultsToStarterType(t *testing.T) {
result, err := Service{}.DryRunCreateCluster(context.Background(), "tdc db create-db-cluster", CreateClusterOptions{
Profile: testProfile(),
DisplayName: "demo-cluster",
ProjectID: "project-1",
})
if err == nil {
t.Fatal("expected missing cluster type to fail")
if err != nil {
t.Fatalf("expected missing cluster type to default to starter: %v", err)
}
if got := apperr.ExitCodeFor(err); got != 2 {
t.Fatalf("expected exit code 2, got %d", got)
found := false
for _, check := range result.Checks {
if check.Name == "cluster_type" && check.Message == validate.ClusterTypeStarter {
found = true
}
}
if !found {
t.Fatalf("expected dry-run checks to use starter, got %#v", result.Checks)
}
}

func TestCreateRejectsUnsupportedClusterType(t *testing.T) {
_, err := Service{}.DryRunCreateCluster(context.Background(), "tdc db create-db-cluster", CreateClusterOptions{
Profile: testProfile(),
DisplayName: "demo-cluster",
ClusterType: "essential",
ProjectID: "project-1",
})
if apperr.CodeFor(err) != "db.unsupported_cluster_type" {
t.Fatalf("expected unsupported cluster type error, got %v", err)
}
}

Expand Down
6 changes: 2 additions & 4 deletions internal/db/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,8 @@ func Required(flagName, value string) error {
}

func ClusterType(value string) error {
if err := Required("--db-cluster-type", value); err != nil {
return err
}
if strings.TrimSpace(value) != ClusterTypeStarter {
trimmed := strings.TrimSpace(value)
if trimmed != "" && trimmed != ClusterTypeStarter {
return apperr.New(
"db.unsupported_cluster_type",
"usage",
Expand Down