diff --git a/docs/guide/cli-reference.md b/docs/guide/cli-reference.md index ce406a3..dec71cf 100644 --- a/docs/guide/cli-reference.md +++ b/docs/guide/cli-reference.md @@ -34,6 +34,13 @@ Spec Engine — it invents no semantics of its own. Exit codes mirror specdrift: | `goldpath db status [--path ]` | What is applied, what is pending — also inlined into `check`. | | `goldpath db bundle [--path ]` | A self-contained migration bundle for environments where the SDK is not installed. | +## Export + +- `goldpath export compose [--path ]` — reads the AppHost (the ONLY definition of the + topology) and writes a `docker-compose.yaml` + Dockerfile beside it. Re-runnable; the + compose file is an EXPORT, never a second source of truth. Exit 1 with the reason when + the AppHost declares no resources or a project has no matching directory. + ## Inspect | Verb | What it does | @@ -55,3 +62,8 @@ goldpath/templates/goldpath-worker kind=worker name=GoldpathWorker - It never edits what the engine would reject: every transform is validated, and a refusal rolls the change back whole. - It calls no LLM. The AI skills call the CLI and the engine, never the reverse (ADR-0004). + +## Help + +- `goldpath help` (also `--help`, `-h`) — the verb list with one line each; `--version`/`-v` + prints the informational version. Unknown verbs exit 2 with usage — never a silent 0. diff --git a/docs/rfc/goldpath-notification.md b/docs/rfc/goldpath-notification.md index 7c36596..f1d01e8 100644 --- a/docs/rfc/goldpath-notification.md +++ b/docs/rfc/goldpath-notification.md @@ -140,7 +140,8 @@ replay to the jobs console. The notification rows ARE the audit. - Insurance's nightly renewal run: 10k notifications rendered + evidence-stamped against a no-op channel — measure rows/s (expectation: template render is string work; the channel dominates in production). -- Render micro-bench: token replacement at p95 (budget: sub-millisecond). +- Render micro-bench: token replacement at p95 (budget: sub-millisecond). Reference + profile: `scripts/bench-notification.sh` → `packages/Goldpath.Notification/ops/notification-benchmarks.md`. - Queue-age under a stopped channel: the backlog drains after recovery without loss or duplicates (the claim proof, notification-flavored). diff --git a/docs/rfc/goldpath-versioning.md b/docs/rfc/goldpath-versioning.md index bf8d80a..3512f23 100644 --- a/docs/rfc/goldpath-versioning.md +++ b/docs/rfc/goldpath-versioning.md @@ -50,7 +50,7 @@ warnings first; a later minor may raise severity, and says so in the guide). No release tag without all three, in the same release PR: -1. **PublicAPI roll:** every package's `Unshipped.txt` rolls into `Shipped.txt` (the +1. **PublicAPI roll (`scripts/roll-shipped.sh`):** every package's `Unshipped.txt` rolls into `Shipped.txt` (the ledger IS the released surface; the roll script lands with the NuGet release work). 2. **CHANGELOG entry** for the train (one file, newest on top). 3. **Upgrade guide:** `docs/upgrades/.md` — step-by-step for every break; diff --git a/docs/strategy/coverage-matrix.md b/docs/strategy/coverage-matrix.md index 4ab0be2..776d5ca 100644 --- a/docs/strategy/coverage-matrix.md +++ b/docs/strategy/coverage-matrix.md @@ -61,6 +61,20 @@ for planning · — deliberately absent (the card does not trigger it). | specdrift CLI path | ✅ | — | | specdrift MCP path (from skills) | 🟡 not exercised in a sample flow | Insurance build runs it | +## Ring C modules (matrix entry pending their sample cards) + +| Capability | CorPay (finance) | Insurance | Telco | +|---|---|---|---| +| Approvals | 🟡 no sample story yet (the api-portal product proves the pattern end to end) | 🔷 endorsement four-eyes candidate | 🔷 tariff-change four-eyes candidate | +| FileExchange | 🟡 no sample story yet | 🔷 bordereaux import candidate | 🔷 CDR drop candidate | + +## Foundational packages (no matrix row — they underpin EVERY cell) + +Abstractions · Sdk · ServiceDefaults · ApiDefaults · Data · Messaging — composed by every +sample and every generated shape; their proof is the nightly golden-manifest matrix, not a +sector story. Named here so the docs-freshness inventory can hold this file to the full +package list. + ## Console panels (post-UI; eyes-on + Playwright per sample) | Panel | CorPay | Insurance | Telco | diff --git a/docs/strategy/golden-manifests-v1.md b/docs/strategy/golden-manifests-v1.md index bfe2b37..a0f7abe 100644 --- a/docs/strategy/golden-manifests-v1.md +++ b/docs/strategy/golden-manifests-v1.md @@ -87,3 +87,25 @@ 2. A combination that blows up in the field → a GM update or a regression manifest (never deleted). 3. A GM change demands a review as serious as a schema/template change (these are the product's contract). 4. Every GM is also an example project usable in sales/demos — narrated by its persona name. + +## 6. The nightly matrix today (docs-freshness gate keeps this table honest) + +Every shape below runs nightly via `scripts/validate-gm.sh` — pack → generate → build → +spec-lint → smoke with real containers. A shape name here and in `nightly.yml` may not +drift apart; the gate fails on either direction. + +| Shape | Arguments | What it proves | +|---|---|---| +| GmOneAuthDefault | (defaults) | the default shape, auth floor up | +| GmOneOpenFlow | `--auth none` | open shape drives the full order flow | +| GmFourSimple | `--db sqlserver --broker none --auth none` | the SqlServer/no-broker quadrant | +| GmFourClean | `--layout clean-architecture …` | four-project split, migrations in Infrastructure | +| GmSixGateway | `--auth none` + `new service` + `new gateway` | multi-head by the adopter's own verbs; routed probe | +| GmBulkOnly | `--features bulk --broker none --auth none` | operational module with ONLY the app database | +| Gm.Dotted | `--broker none --auth none` | dotted solution names (issue #24 regression) | +| GmConsole | `--features bulk --auth none` | the console SERVES with its own bundle | +| GmConsoleAuthed | `--features bulk` | the console's 401 floor branch | +| GmEverything | all eleven features | maximal composition compiles, boots, smokes | +| GmWorkerQueue | worker template | queue-triggered worker shape | +| GmWorkerSchedule | worker `--trigger schedule` | schedule-triggered worker shape | +| GmWorkerJobs | worker `--trigger jobs` | jobs-profile worker shape | diff --git a/packages/README.md b/packages/README.md index 447605c..aa9cb79 100644 --- a/packages/README.md +++ b/packages/README.md @@ -1,6 +1,6 @@ # packages/ -The NuGet train — 19 packages (Abstractions → ServiceDefaults → ApiDefaults → Data → -Messaging → Ring B/C modules), published as `0.1.0-preview.*` with OIDC trusted publishing. +The NuGet train — 23 packages (Abstractions → ServiceDefaults → ApiDefaults → Data → +Messaging → Ring B/C modules + providers), published as `0.1.0-preview.*` with OIDC trusted publishing. Per-module RFCs and status: `docs/rfc/README.md`. Live capability ledger: `docs/strategy/coverage-matrix.md`. diff --git a/scripts/docs-freshness.sh b/scripts/docs-freshness.sh index 7d62ac1..0919271 100755 --- a/scripts/docs-freshness.sh +++ b/scripts/docs-freshness.sh @@ -65,3 +65,83 @@ if offences: sys.exit(1) print("── retired names: none of the retired tools are mentioned") PY + +# ── INVENTORY SYNC (#174): every public surface is NAMED where readers look for it, and +# every numeric claim about a surface matches reality. Same contract as the tests: +# "the docs are stale" is a RED state, never a silent one. +python3 - "$ROOT" <<'PY' +import os, re, sys +root = sys.argv[1] +fail = [] +def read(*parts): + path = os.path.join(root, *parts) + return open(path, encoding="utf-8", errors="replace").read() if os.path.exists(path) else "" + +# 1. Every package is on the live capability ledger. +ledger = read("docs", "strategy", "coverage-matrix.md") +packages = sorted(d for d in os.listdir(os.path.join(root, "packages")) + if d.startswith("Goldpath.") and os.path.isdir(os.path.join(root, "packages", d))) +# The matrix speaks capability SHORT names ("Idempotency"); providers ride their base +# capability's row ("Locking" covers Locking.SqlServer). Either spelling satisfies. +for package in packages: + short = package.removeprefix("Goldpath.") + base = short.split(".")[0] + if package not in ledger and short not in ledger and base not in ledger: + fail.append(f"package {package} missing from docs/strategy/coverage-matrix.md") + +# 2. A doc that COUNTS packages must count them correctly (packages/README said 19 while +# 23 existed — found by this gate's first run). +readme = read("packages", "README.md") +for claim in re.findall(r"(\d+)\s+packages", readme): + if int(claim) != len(packages): + fail.append(f"packages/README.md claims {claim} packages; there are {len(packages)}") + +# 3. Every CLI verb the dispatcher knows is in the CLI reference. +dispatch = read("tools", "Goldpath.Cli", "CliRunner.cs") +verbs = set() +for m in re.finditer(r'\["([a-z]+)"(?:, "([a-z]+)")?', dispatch): + verbs.add(m.group(1) if not m.group(2) else f"{m.group(1)} {m.group(2)}") +verbs -= {"new"} if "new " in " ".join(verbs) else set() +reference = read("docs", "guide", "cli-reference.md") +for verb in sorted(verbs): + if f"goldpath {verb}" not in reference: + fail.append(f"CLI verb 'goldpath {verb}' missing from docs/guide/cli-reference.md") + +# 4. Every golden-manifest shape in the nightly matrix is in the golden-manifests doc. +nightly = read(".github", "workflows", "nightly.yml") +gm_doc = read("docs", "strategy", "golden-manifests-v1.md") +for shape in re.findall(r"- name: (Gm[A-Za-z.]+)", nightly): + if shape not in gm_doc: + fail.append(f"nightly shape {shape} missing from docs/strategy/golden-manifests-v1.md") + +# 5. Every script is referenced SOMEWHERE a reader can find it. +corpus = "" +for scope in ["README.md", "CLAUDE.md"]: + corpus += read(scope) +for base, dirs, names in os.walk(os.path.join(root, "docs")): + for n in names: + if n.endswith(".md"): + corpus += read(os.path.relpath(os.path.join(base, n), root)) +for n in os.listdir(os.path.join(root, ".github", "workflows")): + corpus += read(".github", "workflows", n) +corpus += "".join(read("scripts", n) for n in os.listdir(os.path.join(root, "scripts"))) +for script in sorted(os.listdir(os.path.join(root, "scripts"))): + if script.endswith((".sh", ".py")) and script not in corpus.replace("docs-freshness.sh", script, 0): + others = corpus + if script not in others: + fail.append(f"scripts/{script} is referenced nowhere (README, CLAUDE.md, docs, workflows, other scripts)") + +# 6. Every template is in templates/README.md. +templates_doc = read("templates", "README.md") +for template in sorted(d for d in os.listdir(os.path.join(root, "templates")) + if d.startswith("goldpath-") and os.path.isdir(os.path.join(root, "templates", d))): + if template not in templates_doc: + fail.append(f"template {template} missing from templates/README.md") + +if fail: + print("── inventory sync: the docs stopped telling the truth:") + for f in fail: + print(f" {f}") + sys.exit(1) +print(f"── inventory sync: {len(packages)} packages, {len(verbs)} CLI verbs, nightly shapes, scripts and templates all documented") +PY diff --git a/templates/README.md b/templates/README.md index 54020eb..4ffe851 100644 --- a/templates/README.md +++ b/templates/README.md @@ -3,8 +3,10 @@ Golden-path templates. Current: `goldpath-solution` (GM-1 shape defaults: modular-monolith, vertical-slice, PostgreSQL, RabbitMQ) — with `--layout clean-architecture` generating the four-project split (Domain / Application / Infrastructure / Api; the packages anchor rides -Infrastructure, which also owns migrations). Further shapes arrive per the template RFC -phasing (`docs/rfc/goldpath-template.md`, decision D1). +Infrastructure, which also owns migrations). Also current: `goldpath-worker` — the +headless worker shape (queue / schedule / jobs triggers; probes instead of business +contracts, no OpenAPI artifact). Further shapes arrive per the template RFC phasing +(`docs/rfc/goldpath-template.md`, decision D1). ``` dotnet new install Goldpath.Templates