diff --git a/AGENTS.md b/AGENTS.md index 2d26dcb..28735a2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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 ` +- `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 ` - `tdc db list-db-clusters` - `tdc db list-db-clusters --query 'clusters[].id'` - `tdc db describe-db-cluster --db-cluster-id ` diff --git a/README.md b/README.md index f3cb838..c0384e0 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 ` to create in another accessible project. An explicit empty `--project-id` is rejected instead of falling back to the profile. diff --git a/docs/pingcap-docs/docs b/docs/pingcap-docs/docs index 388e0b6..2db53d0 160000 --- a/docs/pingcap-docs/docs +++ b/docs/pingcap-docs/docs @@ -1 +1 @@ -Subproject commit 388e0b6e0a0e57a4fc231d3ded5c70dbc95a0037 +Subproject commit 2db53d03a29fc15a5a555fe6a2ff20fd59e875d2 diff --git a/docs/spec/done/0006-starter-db-cluster-lifecycle.md b/docs/spec/done/0006-starter-db-cluster-lifecycle.md index f320e86..3bf6d23 100644 --- a/docs/spec/done/0006-starter-db-cluster-lifecycle.md +++ b/docs/spec/done/0006-starter-db-cluster-lifecycle.md @@ -17,15 +17,15 @@ Initial command set: Primary create shape: ```bash -tdc db create-db-cluster --db-cluster-name --db-cluster-type starter -tdc db create-db-cluster --db-cluster-name --db-cluster-type starter --wait +tdc db create-db-cluster --db-cluster-name +tdc db create-db-cluster --db-cluster-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. @@ -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 +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 tdc db list-db-clusters --query 'clusters[].id' tdc db describe-db-cluster --db-cluster-id tdc db delete-db-cluster --db-cluster-id @@ -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 @@ -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`. diff --git a/docs/spec/done/0019-pingcap-tdc-documentation.md b/docs/spec/done/0019-pingcap-tdc-documentation.md index 2797799..9452c30 100644 --- a/docs/spec/done/0019-pingcap-tdc-documentation.md +++ b/docs/spec/done/0019-pingcap-tdc-documentation.md @@ -42,9 +42,8 @@ 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 @@ -52,7 +51,6 @@ ai/tdc/ 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 @@ -60,8 +58,9 @@ ai/tdc/ 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 @@ -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`. @@ -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 @@ -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. @@ -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: @@ -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: @@ -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. diff --git a/e2e/cli_test.go b/e2e/cli_test.go index 6a9fdf0..3f4a280 100644 --- a/e2e/cli_test.go +++ b/e2e/cli_test.go @@ -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 (required)") - createDBCluster.wantStdoutContains("--db-cluster-type (required)") + createDBCluster.wantStdoutContains("[--db-cluster-type ]") + createDBCluster.wantStdoutNotContains("--db-cluster-type (required)") createDBCluster.wantStdoutContains("--project-id ") createDBCluster.wantStdoutNotContains("--project-id (required)") @@ -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", diff --git a/e2e/live_test.go b/e2e/live_test.go index d88a171..c20636d 100644 --- a/e2e/live_test.go +++ b/e2e/live_test.go @@ -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) diff --git a/internal/cli/commands.go b/internal/cli/commands.go index 4e15286..8659645 100644 --- a/internal/cli/commands.go +++ b/internal/cli/commands.go @@ -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 } diff --git a/internal/cli/root_test.go b/internal/cli/root_test.go index b5c003d..5bd95a8 100644 --- a/internal/cli/root_test.go +++ b/internal/cli/root_test.go @@ -262,7 +262,8 @@ func TestHelpUsageShowsRequiredFirstAndOptionalBracketed(t *testing.T) { } for _, want := range []string{ "--db-cluster-name (required)", - "--db-cluster-type (required)", + "[--db-cluster-type ]", + "--db-cluster-type ", "--project-id ", "--monthly-spending-limit-usd-cents ", } { @@ -273,6 +274,12 @@ func TestHelpUsageShowsRequiredFirstAndOptionalBracketed(t *testing.T) { if strings.Contains(stdout, "--project-id (required)") { t.Fatalf("optional --project-id must not be marked required, got:\n%s", stdout) } + if strings.Contains(stdout, "--db-cluster-type (required)") { + t.Fatalf("defaulted --db-cluster-type must not be marked required, got:\n%s", stdout) + } + if !strings.Contains(stdout, `--db-cluster-type `) || !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 { @@ -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) } diff --git a/internal/db/cluster_test.go b/internal/db/cluster_test.go index ed3ee94..0ef40d5 100644 --- a/internal/db/cluster_test.go +++ b/internal/db/cluster_test.go @@ -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) { @@ -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) } } diff --git a/internal/db/validate/validate.go b/internal/db/validate/validate.go index 40d5930..d315adc 100644 --- a/internal/db/validate/validate.go +++ b/internal/db/validate/validate.go @@ -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",