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
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Progress must always be visible

`docs/release-<version>/INDEX.md` is generated from the header table of each story file
`docs/release-<version>/README.md` is generated from the header table of each story file
(`docs/release-<version>/CAP-NNN-*.md`). It must reflect reality
at every point, not only at the end of a release.

Expand All @@ -14,7 +14,7 @@ at every point, not only at the end of a release.
- In the same change, regenerate the API coverage reports: `python3 scripts/update-api-coverage.py` (or
`make coverage`). `docs/release-1.0.0/API-COVERAGE.md` derives every command's and operation's status from the
story statuses, so a story that becomes Implemented updates it.
- Never edit INDEX.md or the API coverage reports by hand. CI runs
- Never edit README.md or the API coverage reports by hand. CI runs
`python3 scripts/update-story-index.py --check docs/release-1.0.0` and `python3 scripts/update-api-coverage.py --check`
and fails when any of them is stale.

Expand Down Expand Up @@ -88,7 +88,7 @@ the updated `THIRD_PARTY_NOTICES.md`. CI fails when the file is stale or a depen
```bash
python3.12 -m venv .venv && .venv/bin/pip install -e '.[dev]'
make check # lint, types, tests, AC coverage, INDEX and API coverage checks
make index # regenerate INDEX.md
make index # regenerate README.md
make coverage # regenerate the API coverage reports (docs/release-*/API-COVERAGE*.md)
make notices # regenerate THIRD_PARTY_NOTICES.md after a dependency change
make docs # regenerate docs/reference.md after a schema change
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ Passwords are never written to `capella.yaml` or printed: give them as `--passwo
| [Examples](docs/release-1.0.0/examples/) | [Annotated reference](docs/release-1.0.0/examples/capella.yaml), [single-node dev](docs/release-1.0.0/examples/single-node-dev.yaml), [GCP production](docs/release-1.0.0/examples/gcp-prod.yaml), [Azure production](docs/release-1.0.0/examples/azure-prod.yaml), [minimal](docs/release-1.0.0/examples/minimal-cluster.yaml) |
| [Migrating from the scale script](docs/migrating-from-scale-script.md) | `scale_capella_cluster.sh` → capellactl |
| [Architecture](docs/release-1.0.0/00-architecture.md) | Design, idempotency rules, test conventions |
| [INDEX.md](docs/release-1.0.0/INDEX.md) | Release plan and user stories |
| [Release README](docs/release-1.0.0/README.md) | Release plan and user stories |
| [CONTRIBUTING.md](CONTRIBUTING.md) | How to build, test and change the project |

Couchbase also publishes an official Terraform provider for Capella; [capellactl or Terraform?](docs/terraform.md) explains when to prefer it.
Expand All @@ -110,7 +110,7 @@ Couchbase also publishes an official Terraform provider for Capella; [capellactl
| [capellactl/importer/](capellactl/importer/), [output/](capellactl/output/), [help/](capellactl/help/) | `import`/`sync` writer; rendering, redaction and passwords; `capellactl help` pages |
| [tests/](tests/) | `unit/`, `contract/` (mocked HTTP), `scenario/` (CLI against [the fake Capella](tests/fakes/capella.py)), `integration/` (real Capella) |
| [scripts/](scripts/) | Generators and CI checks: story index, API coverage, AC coverage, third-party notices |
| [docs/](docs/) | [Architecture](docs/release-1.0.0/00-architecture.md), user stories (`CAP-NNN-*.md`), [INDEX.md](docs/release-1.0.0/INDEX.md), [API coverage](docs/release-1.0.0/API-COVERAGE.md), [runbook](docs/runbook.md), [examples](docs/release-1.0.0/examples/) |
| [docs/](docs/) | [Architecture](docs/release-1.0.0/00-architecture.md), user stories (`CAP-NNN-*.md`), [release plan](docs/release-1.0.0/README.md), [API coverage](docs/release-1.0.0/API-COVERAGE.md), [runbook](docs/runbook.md), [examples](docs/release-1.0.0/examples/) |

Build, test and documentation conventions: [CONTRIBUTING.md](CONTRIBUTING.md) and the [Makefile](Makefile).

Expand Down
2 changes: 1 addition & 1 deletion capellactl/cli/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

def not_yet(story: str, what: str) -> CliExit:
return CliExit(ExitCode.ERROR, f"{what} is not available in this build yet (planned in {story}; see "
"docs/release-1.0.0/INDEX.md)")
"docs/release-1.0.0/README.md)")


# ------------------------------------------------------------------ helpers
Expand Down
4 changes: 2 additions & 2 deletions docs/release-1.0.0/00-architecture.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# capellactl 1.0.0 — Architecture & Technical Decisions

This document is the shared reference for every story in `docs/release-1.0.0`. Stories link back here instead of repeating the language decision, the idempotency model, conventions and the test strategy. The story list and delivery phases are in [INDEX.md](INDEX.md). How to use the tool is in the repository [README.md](../../README.md).
This document is the shared reference for every story in `docs/release-1.0.0`. Stories link back here instead of repeating the language decision, the idempotency model, conventions and the test strategy. The story list and delivery phases are in the release [README.md](README.md). How to use the tool is in the repository [README.md](../../README.md).

API: Capella Management API v4.0, `https://cloudapi.cloud.couchbase.com` ([reference](https://docs.couchbase.com/cloud/management-api-reference/index.html)). Every endpoint, status code, field and enum named in the stories was taken from the published OpenAPI spec (`openapi.external.generated.yaml`, v4.0).

Expand Down Expand Up @@ -420,7 +420,7 @@ Every acceptance criterion `ACn` in story `CAP-NNN` has at least one test named
test_<subject>_ac<n>_<behaviour>
```

in the file named in the story's test table, and is tagged `@pytest.mark.story("CAP-NNN", "AC<n>")`. A CI check (CAP-001) fails if an AC of an `Implemented` story has no tagged test, and lists the open ACs of `In progress` stories, so the story files, INDEX.md and the test suite cannot drift apart.
in the file named in the story's test table, and is tagged `@pytest.mark.story("CAP-NNN", "AC<n>")`. A CI check (CAP-001) fails if an AC of an `Implemented` story has no tagged test, and lists the open ACs of `In progress` stories, so the story files, README.md and the test suite cannot drift apart.

### 12.3 Standard assertions

Expand Down
2 changes: 1 addition & 1 deletion docs/release-1.0.0/API-COVERAGE.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Management API coverage — operational database

> Generated by `scripts/update-api-coverage.py` from the Capella Management API v4.0 spec ([source](https://docs.couchbase.com/cloud/management-api-reference/_attachments/openapi.external.generated.yaml), snapshot of 2026-10-06 in [docs/api](../api/management-api-v4.0-operations.json)), the command dictionary in that script and the **Status** of each story. Every in-scope operation in the spec appears exactly once. Don't edit by hand: change a story's status and run `make coverage`. CI fails when this file is stale. Story progress: [INDEX.md](INDEX.md).
> Generated by `scripts/update-api-coverage.py` from the Capella Management API v4.0 spec ([source](https://docs.couchbase.com/cloud/management-api-reference/_attachments/openapi.external.generated.yaml), snapshot of 2026-10-06 in [docs/api](../api/management-api-v4.0-operations.json)), the command dictionary in that script and the **Status** of each story. Every in-scope operation in the spec appears exactly once. Don't edit by hand: change a story's status and run `make coverage`. CI fails when this file is stale. Story progress: [README.md](README.md).

## Scope

Expand Down
2 changes: 1 addition & 1 deletion docs/release-1.0.0/CAP-001-package-scaffold-cli-ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ As in [00-architecture.md §4](00-architecture.md#4-architecture), plus:
## Implementation notes (as built)
- Verified: wheel installs and runs on Python 3.12 (macOS) and on Python 3.10 and 3.13 (Linux containers). Windows is covered by the CI matrix, which has not run yet because the repository has no remote.
- Container image: runs as uid 10001 and adds 42 MB to `python:3.12-slim`. AC6 originally said 'under 150 MB', which no image can meet because the base alone is about 150 MB uncompressed; the AC now measures what the tool adds (< 50 MB).
- AC5: the coverage check fails for `Implemented` stories and lists the open ACs of `In progress` stories, so stories can be marked `In progress` as soon as work starts (INDEX.md rule).
- AC5: the coverage check fails for `Implemented` stories and lists the open ACs of `In progress` stories, so stories can be marked `In progress` as soon as work starts (README.md rule).

### Tests by AC

Expand Down
2 changes: 1 addition & 1 deletion docs/release-1.0.0/CAP-033-docs-examples-runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ As a **customer engineer new to Capella**, I want clear examples and a short run
## Implementation notes (as built)
- AC5 changed (2026-10-08): the manual timing became an automated end-to-end run of the guide against the fake
Capella (two paths: create a project and cluster, or `init`), run in CI. The 60-minute walkthrough by a new
engineer stays in the release checklist (INDEX.md) as a release check.
engineer stays in the release checklist (README.md) as a release check.
- The cheatsheet was merged into `docs/runbook.md` (2026-10-08) and removed: Part 1 day-2 tasks with expected
output, Part 2 the command reference, Part 3 the 20-minute demo (Azure). README, getting-started and the CAP-042
AC8 test point to the runbook.
Expand Down
17 changes: 11 additions & 6 deletions docs/release-1.0.0/CAP-050-protect-main-branch.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

| Epic | Priority | Points | Depends on | Status |
|---|---|---|---|---|
| E9 Lifecycle & release | P0 | 3 | CAP-001, CAP-043 | In progress |
| E9 Lifecycle & release | P0 | 3 | CAP-001, CAP-043 | Implemented |

## User story
As the **maintainer opening capellactl to the public after release 1.0.0**, I want every change to `main` to arrive through a pull request from a branch that has been reviewed and has passed CI, so that no one, including me or another admin, can push or merge unreviewed code into the branch that releases are tagged from.
Expand Down Expand Up @@ -102,10 +102,15 @@ Add to the 1.0.0 checklist in `scripts/update-story-index.py`: "`main` and `v*`
- ~~Apply the rulesets before the repository becomes public, or as the first step afterwards?~~ Answered (2026-10-21): before, as recommended. If the organisation's plan doesn't allow rulesets on a private repository, `make rulesets` fails with an error from GitHub; apply them as the first step after making it public.

## Implementation notes (as built)
- **Still open:** applying the rulesets to GitHub, then the manual AC1 check. As the repository admin:
`brew install gh && gh auth login`, `make rulesets`, then `make rulesets-check` (green) and
`git push origin HEAD:main` (refused). Add the secret `RULESETS_TOKEN` (a fine-grained token for this repository,
Administration: read-only), which the release workflow needs on a tag. Then set the story to Implemented.
- Applied 2026-10-21 by the repository admin with `make rulesets`, while the repository is still private (the
organisation's plan allows it). `make rulesets-check` is green against GitHub, and the settings read back as
merge commits off and merged branches deleted. AC1's manual check: the admin's `git push origin main` was
refused with `GH013: Repository rule violations`: "Changes must be made through a pull request" and
"6 of 6 required status checks are expected".
- **Not yet done (not an AC):** the repository secret `RULESETS_TOKEN` (a fine-grained token for this repository,
Administration: read-only). Until it exists, every release tag, `-rc` tags included, fails in the `check` job.
Also still manual: Settings → Actions → General → approval for fork pull request workflows from first-time
contributors.
- **Four rulesets instead of two** (`.github/rulesets/`). A ruleset bypass skips every rule in that ruleset, so a
single `main` ruleset with the maintainer as a bypass actor would also let them merge with failing CI. Split:
- `main`: no bypass. Pull request required (no approval count; resolved conversations; squash or rebase), the
Expand Down Expand Up @@ -135,7 +140,7 @@ Add to the 1.0.0 checklist in `scripts/update-story-index.py`: "`main` and `v*`

| AC | Tests |
|---|---|
| AC1 | `test_rulesets_ac1_main_blocks_push_force_and_delete`; manual push check after `make rulesets` (still to do) |
| AC1 | `test_rulesets_ac1_main_blocks_push_force_and_delete`; manual push check (2026-10-21: refused, GH013) |
| AC2 | `test_rulesets_ac2_review_rules`, `test_rulesets_ac2_required_checks_match_ci_matrix` |
| AC3 | `test_rulesets_ac3_codeowners_cover_sensitive_paths` |
| AC4 | `test_rulesets_ac4_release_tags_immutable` |
Expand Down
20 changes: 10 additions & 10 deletions docs/release-1.0.0/INDEX.md → docs/release-1.0.0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ Architecture, domain model, conventions, NFRs and the global Definition of Done:
| Metric | Stories | Story points |
|---|---|---|
| **Total** | **50** | **195** |
| ✅ Implemented | 48 | 187 |
| 🚧 In progress | 2 | 8 |
| ✅ Implemented | 49 | 190 |
| 🚧 In progress | 1 | 5 |
| ⬜ Not started | 0 | 0 |
| Not yet implemented (in progress + not started) | 2 | 8 |
| **Completion** | 96% | 96% |
| Not yet implemented (in progress + not started) | 1 | 5 |
| **Completion** | 98% | 97% |

### By priority

| Priority | Meaning | Stories | Points | Implemented (stories / points) |
|---|---|---|---|---|
| P0 | Must have — release blocker | 43 | 171 | 41 / 163 |
| P0 | Must have — release blocker | 43 | 171 | 42 / 166 |
| P1 | Should have — ships in 1.0 unless explicitly descoped | 7 | 24 | 7 / 24 |

### By epic
Expand All @@ -34,7 +34,7 @@ Architecture, domain model, conventions, NFRs and the global Definition of Done:
| E6 Network access | 1 | 3 | 1 / 3 |
| E7 Data model | 4 | 14 | 4 / 14 |
| E8 Database credentials | 4 | 12 | 4 / 12 |
| E9 Lifecycle & release | 11 | 44 | 9 / 36 |
| E9 Lifecycle & release | 11 | 44 | 10 / 39 |

## Stories by priority

Expand Down Expand Up @@ -84,7 +84,7 @@ Architecture, domain model, conventions, NFRs and the global Definition of Done:
| 40 | ~~[CAP-044](CAP-044-license-and-notices.md)~~ | ~~Licence, notices and third-party attributions~~ | ~~E9 Lifecycle & release~~ | ~~1~~ | ✅ Implemented |
| 41 | ~~[CAP-046](CAP-046-credential-commands.md)~~ | ~~Database credential commands: list, get, create, update~~ | ~~E8 Database credentials~~ | ~~3~~ | ✅ Implemented |
| 42 | ~~[CAP-048](CAP-048-track-resources-by-id.md)~~ | ~~Track projects and clusters by id so renames in the Capella UI are not mistaken for new resources~~ | ~~E9 Lifecycle & release~~ | ~~5~~ | ✅ Implemented |
| 43 | [CAP-050](CAP-050-protect-main-branch.md) | Protect `main`: changes only through a reviewed pull request | E9 Lifecycle & release | 3 | 🚧 In progress |
| 43 | ~~[CAP-050](CAP-050-protect-main-branch.md)~~ | ~~Protect `main`: changes only through a reviewed pull request~~ | ~~E9 Lifecycle & release~~ | ~~3~~ | ✅ Implemented |

### P1 — Should have — ships in 1.0 unless explicitly descoped (7 stories, 24 points)

Expand All @@ -108,7 +108,7 @@ Stories are delivered in phases; each phase ends with something an operator can
| Phase 1 — Project & cluster | ~~CAP-009~~, ~~CAP-039~~, ~~CAP-040~~, ~~CAP-010~~, ~~CAP-011~~, ~~CAP-035~~, ~~CAP-012~~, ~~CAP-038~~, ~~CAP-041~~, ~~CAP-049~~, ~~CAP-013~~, ~~CAP-014~~, ~~CAP-022~~ | 53 | 13/13 (53 pts) | ✅ Done | Answer the init wizard, then create, edit, protect and delete a project and cluster with its IP allowlist, idempotently |
| Phase 2 — Scale & hibernate | ~~CAP-015~~, ~~CAP-016~~, ~~CAP-017~~, ~~CAP-018~~, ~~CAP-019~~, ~~CAP-020~~, ~~CAP-021~~ | 26 | 7/7 (26 pts) | ✅ Done | Scale clusters out, in and up safely; power them off and on; run a weekly hibernation schedule |
| Phase 3 — Data model & access | ~~CAP-023~~, ~~CAP-024~~, ~~CAP-025~~, ~~CAP-026~~, ~~CAP-027~~, ~~CAP-046~~, ~~CAP-028~~, ~~CAP-029~~ | 26 | 8/8 (26 pts) | ✅ Done | Buckets, scopes, collections and least-privilege database credentials, ready for applications |
| Phase 4 — Adopt, harden & ship | ~~CAP-030~~, ~~CAP-036~~, ~~CAP-047~~, ~~CAP-048~~, ~~CAP-031~~, ~~CAP-042~~, CAP-032, ~~CAP-044~~, ~~CAP-043~~, ~~CAP-045~~, ~~CAP-033~~, CAP-050 | 47 | 10/12 (39 pts) | 🚧 Current | Import existing clusters with a no-change round trip, one-command build and teardown, drift reporting, nightly integration gate and customer docs |
| Phase 4 — Adopt, harden & ship | ~~CAP-030~~, ~~CAP-036~~, ~~CAP-047~~, ~~CAP-048~~, ~~CAP-031~~, ~~CAP-042~~, CAP-032, ~~CAP-044~~, ~~CAP-043~~, ~~CAP-045~~, ~~CAP-033~~, ~~CAP-050~~ | 47 | 11/12 (42 pts) | 🚧 Current | Import existing clusters with a no-change round trip, one-command build and teardown, drift reporting, nightly integration gate and customer docs |

## Stories by phase

Expand Down Expand Up @@ -180,7 +180,7 @@ _Buckets, scopes, collections and least-privilege database credentials, ready fo
| 7 | [CAP-028](CAP-028-password-rotation.md) | Password rotation | P0 | E8 Database credentials | 3 | CAP-027, CAP-008 | ✅ Implemented |
| 8 | [CAP-029](CAP-029-delete-database-credential.md) | Delete database credential | P0 | E8 Database credentials | 1 | CAP-027 | ✅ Implemented |

### Phase 4 — Adopt, harden & ship — 🚧 Current (10/12 stories, 39/47 points)
### Phase 4 — Adopt, harden & ship — 🚧 Current (11/12 stories, 42/47 points)

_Import existing clusters with a no-change round trip, one-command build and teardown, drift reporting, nightly integration gate and customer docs._

Expand All @@ -197,7 +197,7 @@ _Import existing clusters with a no-change round trip, one-command build and tea
| 9 | [CAP-043](CAP-043-build-distributables.md) | One-command build of the distributables (source release) | P0 | E9 Lifecycle & release | 3 | CAP-001, CAP-044 | ✅ Implemented |
| 10 | [CAP-045](CAP-045-standalone-executables.md) | Standalone executables for macOS, Linux and Windows | P1 | E9 Lifecycle & release | 5 | CAP-043, CAP-044 | ✅ Implemented |
| 11 | [CAP-033](CAP-033-docs-examples-runbook.md) | Examples, runbook & customer documentation | P0 | E9 Lifecycle & release | 3 | CAP-030 | ✅ Implemented |
| 12 | [CAP-050](CAP-050-protect-main-branch.md) | Protect `main`: changes only through a reviewed pull request | P0 | E9 Lifecycle & release | 3 | CAP-001, CAP-043 | 🚧 In progress |
| 12 | [CAP-050](CAP-050-protect-main-branch.md) | Protect `main`: changes only through a reviewed pull request | P0 | E9 Lifecycle & release | 3 | CAP-001, CAP-043 | ✅ Implemented |

## Release checklist

Expand Down
2 changes: 1 addition & 1 deletion scripts/update-api-coverage.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ def count(rows: list[dict[str, Any]]) -> list[int]:
"[docs/api](../api/management-api-v4.0-operations.json)), the command dictionary in that script and the "
"**Status** of each story. Every in-scope operation in the spec appears exactly once. Don't edit by hand: "
"change a story's status and run `make coverage`. CI fails when this file is stale. Story progress: "
"[INDEX.md](INDEX.md).\n")
"[README.md](README.md).\n")
w("## Scope\n")
w(f"The spec has **{len(all_ops)} operations**. Release 1.0.0 is about the operational database, so this report "
f"covers the **{len(ops)} operational-database operations**. Two groups are left out:\n")
Expand Down
8 changes: 4 additions & 4 deletions scripts/update-story-index.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Regenerate docs/<release>/INDEX.md from the story files' header tables.
"""Regenerate docs/<release>/README.md from the story files' header tables.

Each story file (CAP-NNN-*.md) starts with:

Expand All @@ -20,7 +20,7 @@
Adapted from omni-data-bus/scripts/update-story-index.py.

Usage: python3 scripts/update-story-index.py [docs/release-1.0.0]
python3 scripts/update-story-index.py --check [docs/release-1.0.0] # CI: fail if INDEX.md is stale
python3 scripts/update-story-index.py --check [docs/release-1.0.0] # CI: fail if README.md is stale
"""
import re
import sys
Expand Down Expand Up @@ -249,7 +249,7 @@ def main() -> None:
if sorted(phased) != [s["id"] for s in stories]:
sys.exit(f"every story must be in exactly one phase; check RELEASES: {sorted(set(s['id'] for s in stories) ^ set(phased))}")
rendered = render(release, stories)
index = release_dir / "INDEX.md"
index = release_dir / "README.md"
if check:
current = index.read_text(encoding="utf-8") if index.exists() else ""
if current != rendered:
Expand All @@ -259,7 +259,7 @@ def main() -> None:
return
index.write_text(rendered, encoding="utf-8")
live = [s for s in stories if s["status"] != "Descoped"]
print(f"INDEX.md updated: {len(live)} stories, {sum(s['points'] for s in live)} points"
print(f"README.md updated: {len(live)} stories, {sum(s['points'] for s in live)} points"
+ (f" ({len(stories) - len(live)} descoped)" if len(live) < len(stories) else ""))


Expand Down
Loading