feat: full-stack MCP surface — client template, npm CI, 13 repos

- build.py: vendor templates/client into mcp/templates/client with
  client.json manifest (16 kinds); register drone-npm as a third CI shape
  (types: pypi/npm/docker); root *.py stale doc numbers fixed
- registry.py: registry://client resource + list_client_components /
  get_client_component / get_client_template tools; build prompt gains the
  client scaffold step (docs://modules/ui_admin); compliance prompt covers
  npm packages; drone docs describe all three shapes
- repos.json (+mcp): doc-weaver, react-auth, react-openapi (npm Type B,
  package.json version source), vet (app Type C, VERSION)
- docs: 90_drone_pipelines gains Type B npm section; 91_versioning covers
  package.json as a version source; 94_repository_inventory gains the npm
  package table and services/apps Type C; lib index updated

Verified: bundle regen (24 modules, client 16, drone 3, repos 13); registry
client readers exercised against the vendored bundle.
This commit is contained in:
2026-10-20 12:02:31 +05:30
parent 406affcfcf
commit d7c344c9aa
44 changed files with 3251 additions and 94 deletions

View File

@@ -3,17 +3,19 @@
## Summary
Every Aetos repository ships a Drone CI definition at `.drone.yml`. There is
exactly **one standard per repository**, and there are **two types of file**,
exactly **one standard per repository**, and there are **three types of file**,
chosen by what the repository produces:
| Type | File | Produces | Repos |
|------|------|----------|-------|
| **A — PyPI package** | `templates/drone/python-lib.yaml` | A wheel + sdist on the private index | `dagpipe`, `doc-forge`, `hexa`, `mail-intake`, `mongo-ops`, `omniread`, `openapi-first`, `py-jwt` |
| **B — Docker deployment** | `templates/drone/service-docker.yaml` | An image pushed to the registry and run as a container | `auth-server` |
| **B — npm package** | `templates/drone/npm-lib.yaml` | A package published to the private npm registry | `doc-weaver`, `react-auth`, `react-openapi` |
| **C — Docker deployment** | `templates/drone/service-docker.yaml` | An image pushed to the registry and run as a container | `auth-server`, `vet` |
Both templates run on `platform: linux / arm64` with the workspace at
`/drone/src`. Copy the matching template to `<repo>/.drone.yml`; the Type A file
needs no edits, the Type B file needs its placeholders filled in (below).
All templates run on `platform: linux / arm64` with the workspace at
`/drone/src`. Copy the matching template to `<repo>/.drone.yml`; the Type A and
Type B files need no edits, the Type C file needs its placeholders filled in
(below).
> Conventions: `.drone.yml` (not `.yaml`), one file per repo, secrets injected
> with `from_secret` — never commit credentials.
@@ -22,7 +24,7 @@ needs no edits, the Type B file needs its placeholders filled in (below).
## Type A — PyPI package (`python-lib.yaml`)
For internal libraries. The file contains **two pipelines**:
For internal Python libraries. The file contains **two pipelines**:
1. **`build-and-publish-pypi`** — triggered on `tag`:
- `check-version` — **guard**: fail unless `$DRONE_TAG` equals the version in
@@ -48,7 +50,30 @@ Secrets: `PIP_REPO_URL`, `PIP_USERNAME`, `PIP_PASSWORD`.
---
## Type B — Docker deployment (`service-docker.yaml`)
## Type B — npm package (`npm-lib.yaml`)
For internal npm libraries (the React packages). One pipeline,
**`build-and-publish-npm`**, triggered on `tag`:
- `check-version` — **guard**: fail unless `$DRONE_TAG` equals the version in
`package.json`; query `$NPM_REPO_URL` for `name@version`; if it already
exists, exit `78` and skip the whole build.
- `quality-gate` — write `.npmrc` from the secrets, `npm install`, then
`npm run quality-gate`.
- `build-package` — `npm install && npm run build` into `dist/`.
- `publish-to-private-npm` — `npm publish --access restricted` to
`$NPM_REPO_URL`.
Secrets: `NPM_REPO_URL`, `NPM_USERNAME`, `NPM_PASSWORD`. Auth is a generated
`.npmrc` (`registry=` + `_auth=` base64 of `user:pass`) written from secrets at
runtime — the committed `.npmrc` in a repo only points at the registry URL.
- **Ship as-is.** Name and version come from `package.json`, so the same file
is reused verbatim across every npm package.
---
## Type C — Docker deployment (`service-docker.yaml`)
For a service that ships a container image — a long-running API/worker, e.g.
`auth-server`. The file contains **one pipeline**, `build-and-deploy-service`,
@@ -86,15 +111,18 @@ Placeholders to fill in per repo:
## What not to do
- Do not hand-roll a third shape. A repo is either Type A or Type B, never both.
- Do not hand-roll a fourth shape. A repo is exactly one of Type A, B, or C,
never combinations.
- Do not inline secrets; always use `from_secret`.
- Do not drop the `check-version` / `check-remote-image` skip — it guards the
index and the running container against duplicate publishes.
- Do not drop the tag↔version guard. A package needs its `pyproject.toml`
version to equal the tag; a service needs a root `VERSION` file that equals
the tag. See [91 – Versioning & Changelog](91_versioning_and_changelog.md).
- Do not drop the tag↔version guard. A Python package needs its
`pyproject.toml` version to equal the tag; an npm package needs its
`package.json` version; a service needs a root `VERSION` file. See
[91 – Versioning & Changelog](91_versioning_and_changelog.md).
- Do not rename the pipelines; tooling and runbooks key off
`build-and-publish-pypi` and `build-and-deploy-service`.
`build-and-publish-pypi`, `build-and-publish-npm`, and
`build-and-deploy-service`.
---

View File

@@ -10,7 +10,7 @@ a Drone guard enforces that equality before anything is published. Releases are
cut manually; a tag is what triggers the pipeline.
This page pairs with [90 – Drone Pipelines](90_drone_pipelines.md): the version
and tag contract is exactly what those two CI shapes consume.
and tag contract is exactly what those three CI shapes consume.
---
@@ -19,6 +19,7 @@ and tag contract is exactly what those two CI shapes consume.
| Artifact | Source of truth | Notes |
|----------|-----------------|-------|
| Library (PyPI package) | `version` in `pyproject.toml` | `check-version` also reads it to skip duplicates |
| Library (npm package) | `version` in `package.json` | `check-version` also queries the registry to skip duplicates |
| Service (Docker) | `VERSION` file at the repo root | one line, `x.y.z`; the service **reads** it (build-arg) — never hardcode the version |
Rules:
@@ -116,7 +117,8 @@ bump travels with the change and only the tag is added afterwards:
1. **In the MR** — reconcile `[Unreleased]` against the change (see
[Reconcile before releasing](#reconcile-before-releasing)) and get the user's
approval for any additions. Then set the intended release version: `version`
in `pyproject.toml` (or the `VERSION` file) to `x.y.z`, and promote the
in `pyproject.toml` or `package.json` (or the `VERSION` file) to `x.y.z`,
and promote the
changelog (below). Changelog entries and link references name the
**version/tag**, never a commit id.
2. **Merge or squash** the MR into `main`.
@@ -133,8 +135,10 @@ After the user confirms the MR is merged/squashed and says to proceed:
1. **Wait** for that explicit go-ahead.
2. Find the commit that introduced the version bump:
- packages:
- Python packages:
`git log -S 'version = "x.y.z"' -n 1 --format='%H %s' -- pyproject.toml`
- npm packages:
`git log -S '"version": "x.y.z"' -n 1 --format='%H %s' -- package.json`
- services:
`git log -S 'x.y.z' -n 1 --format='%H %s' -- VERSION`
3. Confirm that commit's version file equals `x.y.z`, then tag **only** that
@@ -154,13 +158,14 @@ After the user confirms the MR is merged/squashed and says to proceed:
on top.
3. In the MR: add the two compare-link references at the bottom of the
changelog, naming the version/tag (never a commit id).
4. In the MR: set the version file — `pyproject.toml` (`version = "x.y.z"`) or
`VERSION` — to the intended release version.
4. In the MR: set the version file — `pyproject.toml` (`version = "x.y.z"`),
`package.json` (`"version": "x.y.z"`), or `VERSION` — to the intended
release version.
5. Merge or squash the MR into `main`.
6. Create the tag on the resulting commit: lightweight `git tag x.y.z`.
7. Push the tag (`git push origin x.y.z`). Watch Drone: Type A publishes the
wheel/sdist, Type B builds and deploys the image; both first assert
`tag == version`.
wheel/sdist, Type B publishes the npm package, Type C builds and deploys
the image; all three first assert `tag == version`.
8. Do not reuse the tag if the run fails — fix and cut `x.y.(z+1)`.
---

View File

@@ -5,7 +5,7 @@
The canonical list of Aetos repositories covered by the CI and versioning
standards, with the artifact each one produces, its version source, and its
Drone shape. Every entry maps to the contract in
[91 – Versioning & Changelog](91_versioning_and_changelog.md) and the two CI
[91 – Versioning & Changelog](91_versioning_and_changelog.md) and the three CI
files in [90 – Drone Pipelines](90_drone_pipelines.md).
The same data is published machine-readably as the `registry://repos` MCP
@@ -14,7 +14,7 @@ Paths are relative to the `aetos/` workspace root.
---
## Packages (Type A)
## Packages (Type A — PyPI)
A wheel + sdist published to the private index. Version source is
`pyproject.toml`; Drone file is `python-lib.yaml` (tag-triggered).
@@ -32,14 +32,28 @@ A wheel + sdist published to the private index. Version source is
---
## Services (Type B)
## Packages (Type B — npm)
A package published to the private npm registry (`npm.aetoskia.com`). Version
source is `package.json`; Drone file is `npm-lib.yaml` (tag-triggered).
| Repo | Path | Docs | MCP server |
|------|------|------|------------|
| `doc-weaver` | `doc-weaver` | lib, wiki | `doc-weaver:8010` |
| `react-auth` | `react/react-auth` | lib, wiki | `react-auth:8011` |
| `react-openapi` | `react/react-openapi` | lib, wiki | `react-openapi:8012` |
---
## Services & Apps (Type C)
A container image pushed to the registry and run on the Docker host. Version
source is the `VERSION` file; Drone file is `service-docker.yaml`.
| Repo | Path | Trigger | Docs |
|------|------|---------|------|
| `auth-server` | `auth-server` | tag | api, lib, wiki |
| Repo | Path | Type | Trigger | Docs |
|------|------|------|---------|------|
| `auth-server` | `auth-server` | service | tag | api, lib, wiki |
| `vet` | `react/vet` | app | tag | lib, wiki |
Tag-triggered and enforces `tag == VERSION`.
@@ -49,8 +63,9 @@ Tag-triggered and enforces `tag == VERSION`.
| Type | Version source | Changelog | Drone |
|------|----------------|-----------|-------|
| package | `pyproject.toml` | `CHANGELOG.md` | `python-lib.yaml` |
| service | `VERSION` | `CHANGELOG.md` | `service-docker.yaml` |
| package (PyPI) | `pyproject.toml` | `CHANGELOG.md` | `python-lib.yaml` |
| package (npm) | `package.json` | `CHANGELOG.md` | `npm-lib.yaml` |
| service / app | `VERSION` | `CHANGELOG.md` | `service-docker.yaml` |
Every covered repository must be able to satisfy `tag == version`:
the version file (never a commit id) is bumped in the release MR, the MR is

View File

@@ -44,8 +44,8 @@ and an optional pipelines layer.
## Delivery & Meta
- **Drone Pipelines** — the two CI shapes: a PyPI/npm package or a Docker
deployment.
- **Drone Pipelines** — the three CI shapes: a PyPI package, an npm package,
or a Docker deployment.
- **Versioning & Changelog** — SemVer, the tag↔version contract, and the
release changelog.
- **Repository Inventory** — every covered repo, its type, version source, and