Define and implement multi-repo onboarding mechanism (WP-0006-T07)

specs/TrustServiceOnboarding.md defines the mechanism: a Phase Manifest
file is committed to the declaring repo (durable, independently
foldable forever) and separately registered with the hosted service;
once registered, the Ledger's live authoritative copy is the hosted
service only, not a second competing file. Licensor token bootstrapping
is explicitly out of scope here (a WP-0008-T01 governance action).

scripts/trf_onboard.py: a dependency-light CLI (stdlib urllib +
target_revenue.validation only, no FastAPI/psycopg needed to onboard a
Phase) with validate/register-phase/append-entry/status subcommands.
The Licensor token is read only from a named environment variable,
never accepted as a literal argument.

tests/test_trf_onboard.py (4 tests, no network/Docker) proves
invalid-manifest and missing-token-env cases fail before any HTTP
attempt, by monkeypatching the request function to raise if called.
tests/test_onboarding_hosted.py (1 Docker-gated test) runs an actual
uvicorn server on a real socket and drives the full
register -> append -> status round trip through the CLI as an external
repo would invoke it.
This commit is contained in:
tegwick 2026-07-29 22:00:34 +02:00
parent 45b9fbd765
commit ee4cf14cbc
6 changed files with 530 additions and 2 deletions

View file

@ -78,7 +78,7 @@ The concept's §13 now defines a **Global Contingency Share Determination Rule**
| [TREV-WP-0003](workplans/TREV-WP-0003-normative-core-extraction.md) | Extract stable normative core docs — **finished**, reviewed and accepted 2026-07-29 |
| [TREV-WP-0004](workplans/TREV-WP-0004-global-jurisdiction-research.md) | Global jurisdictional research backing the License/CUA candidates — **finished**, T10 synthesis accepted 2026-07-29 with alpha/beta working defaults (full legal review deferred until out of beta — see `SCOPE.md` §1) |
| [TREV-WP-0005](workplans/TREV-WP-0005-enforcement-network-research.md) | Enforcement Network legal feasibility research — **finished**, T10 synthesis accepted 2026-07-29 on the same alpha/beta basis (Japan's Article 12 risk remains explicitly unresolved) |
| [TREV-WP-0006](workplans/TREV-WP-0006-trust-service-implementation.md) | Hosted Trust Service reference implementation (PRD Phase 4b) — active; T01 (PRD), T02 (ADR-0002, accepted), T03 (registries), T04 (Ledger append API), T05 (Metrics), T06 (Conversion Attestation) done; T07 (multi-repo onboarding) next |
| [TREV-WP-0006](workplans/TREV-WP-0006-trust-service-implementation.md) | Hosted Trust Service reference implementation (PRD Phase 4b) — active; T01 (PRD), T02 (ADR-0002, accepted), T03 (registries), T04 (Ledger append API), T05 (Metrics), T06 (Conversion Attestation), T07 (onboarding mechanism, `specs/TrustServiceOnboarding.md` + `scripts/trf_onboard.py`) done; T08 (hosted conformance suite) next |
| [TREV-WP-0007](workplans/TREV-WP-0007-degeneration-policy-and-canonical-profiles.md) | Degeneration policy + canonical monetization profile catalog — active, not yet started |
| [TREV-WP-0008](workplans/TREV-WP-0008-governance-and-pilot-rollout.md) | Governance formalization + pilot rollout across `coulomb-loop`/`net-kingdom`/`helix-forge`/`railiance-*` — active, not yet started; real Phase declarations gated behind T05 |

136
scripts/trf_onboard.py Normal file
View file

@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""Trust Service onboarding CLI (WP-0006-T07).
Mechanism only see `specs/TrustServiceOnboarding.md` for the full
process this wraps. Deliberately dependency-light: stdlib `urllib` for
HTTP (no `requests`/`httpx` requirement) plus `target_revenue.validation`
(already a core, non-service dependency) for offline pre-flight checks
a repo onboarding a Phase should not need to install FastAPI/psycopg just
to register one.
Every subcommand that submits data validates it offline first, with the
same check the hosted service itself runs at registration
(`validation.py`), so a rejection is caught locally with an identical
field-by-field diff instead of only being discovered via a failed network
call (specs/TrustServiceOnboarding.md §3 steps 2 and 4).
"""
from __future__ import annotations
import argparse
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(REPO_ROOT / "src"))
from target_revenue import validation # noqa: E402
def _load(path: str) -> dict:
with open(path, encoding="utf-8") as f:
return json.load(f)
def _request(url: str, method: str, token: str | None = None, body: dict | None = None) -> dict:
data = json.dumps(body).encode("utf-8") if body is not None else None
headers = {"Content-Type": "application/json"}
if token:
headers["Authorization"] = f"Bearer {token}"
req = urllib.request.Request(url, data=data, headers=headers, method=method)
try:
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode("utf-8"))
except urllib.error.HTTPError as exc:
detail = exc.read().decode("utf-8")
raise SystemExit(f"{method} {url} -> HTTP {exc.code}: {detail}") from exc
def _token_from_env(env_var: str) -> str:
token = os.environ.get(env_var)
if not token:
raise SystemExit(
f"environment variable {env_var!r} is not set — the Licensor "
"API token must never be passed on the command line or "
"committed to the repo (specs/TrustServiceOnboarding.md §2)"
)
return token
def cmd_validate(args: argparse.Namespace) -> None:
manifest = _load(args.manifest)
try:
validation.validate_phase_manifest(manifest)
except validation.ConformanceError as exc:
print("REJECTED:", "; ".join(exc.errors), file=sys.stderr)
raise SystemExit(1)
print(f"OK: {manifest['phase']['id']} is schema-conformant")
def cmd_register_phase(args: argparse.Namespace) -> None:
manifest = _load(args.manifest)
try:
validation.validate_phase_manifest(manifest)
except validation.ConformanceError as exc:
print("REJECTED (offline check, no network call made):", "; ".join(exc.errors), file=sys.stderr)
raise SystemExit(1)
token = _token_from_env(args.token_env)
result = _request(f"{args.url}/phases", "POST", token=token, body=manifest)
print(json.dumps(result, indent=2))
def cmd_append_entry(args: argparse.Namespace) -> None:
entry = _load(args.entry)
token = _token_from_env(args.token_env)
result = _request(
f"{args.url}/phases/{args.phase_id}/ledger", "POST", token=token, body=entry
)
print(json.dumps(result, indent=2))
def cmd_status(args: argparse.Namespace) -> None:
result = _request(f"{args.url}/phases/{args.phase_id}/metrics", "GET")
print(json.dumps(result, indent=2))
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(prog="trf_onboard", description=__doc__)
sub = parser.add_subparsers(dest="command", required=True)
p_validate = sub.add_parser("validate", help="offline-validate a Phase Manifest file, no network call")
p_validate.add_argument("manifest")
p_validate.set_defaults(func=cmd_validate)
p_register = sub.add_parser("register-phase", help="validate offline, then POST /phases")
p_register.add_argument("--url", required=True, help="Trust Service base URL")
p_register.add_argument("--token-env", required=True, help="env var holding the Licensor API token")
p_register.add_argument("--manifest", required=True)
p_register.set_defaults(func=cmd_register_phase)
p_append = sub.add_parser("append-entry", help="POST a Ledger entry for an already-registered Phase")
p_append.add_argument("--url", required=True)
p_append.add_argument("--token-env", required=True)
p_append.add_argument("--phase-id", required=True)
p_append.add_argument("--entry", required=True)
p_append.set_defaults(func=cmd_append_entry)
p_status = sub.add_parser("status", help="GET /phases/{id}/metrics (public, no token)")
p_status.add_argument("--url", required=True)
p_status.add_argument("--phase-id", required=True)
p_status.set_defaults(func=cmd_status)
return parser
def main(argv: list[str] | None = None) -> None:
parser = build_parser()
args = parser.parse_args(argv)
args.func(args)
if __name__ == "__main__":
main()

View file

@ -0,0 +1,106 @@
# Trust Service Onboarding: Multi-Repo Registration Mechanism
Status: Draft v0.1
Date: 2026-07-29
Workplan: `workplans/TREV-WP-0006-trust-service-implementation.md` T07
Primary artifacts: `specs/PhaseManifestSpecification.md`, `specs/TargetLedgerSpecification.md`, `specs/TrustServiceProductRequirementsDocument.md`, `docs/adr/ADR-0002-hosted-trust-service-stack.md`, `scripts/trf_onboard.py`
**Scope note:** this document defines the **mechanism** by which any repo
registers a Phase against a running hosted Trust Service instance. It does
**not** select which repos or Phases go first — that is
`workplans/TREV-WP-0008-governance-and-pilot-rollout.md` T02 (repo/Phase
survey) and T03 (draft pilot manifests), which use this mechanism rather
than redefine it. It also does not authorize any real Phase to go live —
that remains gated behind WP-0008-T05 regardless of how well this
mechanism works.
---
## 1. What a repo commits to itself vs. submits to the Trust Service
A recurring confusion this document exists to close: a Phase Manifest is
**not** solely a Trust Service database row. It has two authoritative
copies with different roles, and both are required, not just one:
| Artifact | Lives in | Role |
|---|---|---|
| Phase Manifest file (e.g. `trf/phase-<slug>.json`) | The declaring repo's own version control, committed | The repo's own durable, git-history-backed record of what it declared and when — survives independent of any Trust Service instance's uptime or even existence (TSD §1.2's offline-verifiability property applies here too: this file alone, plus its ledger export, must remain independently foldable forever). |
| The same Phase Manifest, registered | The hosted Trust Service's `phase_manifests` table (`migrations/0001_registries.sql`) | The multi-tenant, queryable, publicly-servable copy other parties (Customers, auditors, Enforcement Partners) read without needing access to the declaring repo at all. |
| Target Ledger entries | The hosted Trust Service's `ledger_entries` table only | Per ADR-0002, the Ledger's authoritative live copy is the hosted service once a Phase is registered there — a repo does **not** also maintain a second, competing ledger file post-registration (that would reintroduce exactly the "which copy is authoritative" ambiguity Stage 0's single-golden-fixture model never had to answer). A repo *may* keep periodic exports for its own archival/offline-verification purposes (§4 below), but those are copies, not a second source of truth. |
| Extension registrations, Conversion Attestation | The hosted Trust Service, per T03/T06 | Same reasoning as the Ledger. |
**Rule of thumb:** anything that must exist and remain meaningful even if
this Trust Service instance is retired, replaced, or briefly down belongs
committed to the repo (the Manifest declaration itself). Anything that
must be a single, live, multi-party-queryable authority belongs
exclusively in the hosted service once registered (the Ledger).
## 2. Licensor bootstrapping (out of band, not part of this mechanism)
Before any repo can register a Phase, its Licensor identity must already
be resolved (`workplans/TREV-WP-0008-governance-and-pilot-rollout.md` T01)
and a row must exist in the `licensors` table with an issued API token
(`migrations/0001_registries.sql`). This document assumes that step has
already happened; it is a governance action, not a mechanical one, and is
explicitly out of this task's scope. The token itself must never be
committed to the repo — it is a deployment secret (CI secret store,
environment variable at declaration time), referenced by name in the
repo's own tooling, never by value.
## 3. Step-by-step mechanism
1. **Author the Phase Manifest locally**, conforming to
`specs/PhaseManifestSpecification.md` and
`schemas/phase_manifest.schema.json`, following the shape already
established by `examples/phase-001/manifest.json`.
2. **Validate offline before doing anything else**
`python -m target_revenue.validation` equivalent via
`scripts/trf_onboard.py validate <manifest-path>` (wraps
`validation.validate_phase_manifest`, the same check the hosted
service itself runs at registration, so a rejection is caught locally
with the identical field-by-field diff rather than discovered only via
a failed network call).
3. **Commit the Manifest file to the repo** before registering it — this
preserves a git-history record of intent to declare, independent of
whether registration succeeds on the first attempt.
4. **Register**: `scripts/trf_onboard.py register-phase --url <trust-service-url> --token-env <ENV_VAR_NAME> --manifest <path>`. Performs the same offline validation as step 2 again (defense in depth — a manifest edited after step 2 but before this step must still be caught), then `POST /phases`.
5. **Append Ledger entries as commercial payments are recognized.** In
practice this step is automated by whatever billing/payment pipeline
the product line already uses, not run manually per payment —
`scripts/trf_onboard.py append-entry --url ... --token-env ... --phase-id ... --entry <path>` is the mechanism that pipeline calls, or a template for it.
6. **Monitor via the public, unauthenticated read endpoints**
`scripts/trf_onboard.py status --url ... --phase-id ...` (wraps
`GET /phases/{id}/metrics`) — no token needed for this step, since
metrics are public per FR-9/FR-10.
7. **Conversion and Attestation require no onboarding action at all.**
Per WP-0006-T06, the moment the ledger fold reaches
`Outstanding Target = 0`, conversion is already true; the Trust Service
publishes the Attestation on first observation. Nothing in this
mechanism gates on, waits for, or triggers that separately.
## 4. Offline verifiability remains intact after hosting
Any party — including the declaring repo itself, independent of the
Trust Service's continued availability — can export a Phase's current
Ledger via `GET /phases/{id}/ledger` (public) and independently recompute
Outstanding Target with `target_revenue.fold.fold_outstanding_target`
against the committed Manifest file. This is the same offline-first
property `docs/adr/ADR-0002-hosted-trust-service-stack.md` and
`specs/TrustServiceProductRequirementsDocument.md` §3 require the hosted
service to preserve, not a new guarantee invented for onboarding — this
section just makes explicit that a repo can exercise it as part of its own
routine (e.g., a periodic CI job that exports and re-verifies), not only a
theoretical possibility.
## 5. Explicit non-goals
- Selecting which of `coulomb-loop`, `net-kingdom`, `helix-forge`, or a
`railiance-*` repo registers first, or which Milestone Release qualifies
`workplans/TREV-WP-0008-governance-and-pilot-rollout.md` T02/T03.
- Resolving Licensor identity — WP-0008-T01.
- Authorizing any registration performed via this mechanism to bind real
money or a real License header change — WP-0008-T05, unaffected by this
mechanism existing or working correctly.
- A CI/CD template for automated payment-pipeline integration — a
reasonable future deliverable once a real pilot Phase's billing
pipeline is known, not invented speculatively here.

View file

@ -0,0 +1,173 @@
"""End-to-end test: scripts/trf_onboard.py against a real, running hosted
Trust Service instance (WP-0006-T07), not just FastAPI's in-process
TestClient this is the one test in the suite that exercises an actual
HTTP round trip over a real socket, since the onboarding CLI uses
`urllib` against a real URL rather than an ASGI transport.
Same ephemeral, disposable Postgres-via-Docker pattern as the other
hosted tests (never the shared state-hub instance).
"""
from __future__ import annotations
import json
import shutil
import subprocess
import threading
import time
import uuid
from pathlib import Path
import pytest
psycopg = pytest.importorskip("psycopg")
pytest.importorskip("fastapi")
uvicorn = pytest.importorskip("uvicorn")
from conftest import golden_manifest # noqa: E402
REPO_ROOT = Path(__file__).resolve().parents[1]
MIGRATIONS = [
REPO_ROOT / "migrations" / "0001_registries.sql",
REPO_ROOT / "migrations" / "0002_ledger.sql",
REPO_ROOT / "migrations" / "0003_attestations.sql",
]
pytestmark = pytest.mark.skipif(
shutil.which("docker") is None, reason="docker not available"
)
@pytest.fixture(scope="module")
def pg_container():
name = f"trf-test-pg-onboard-{uuid.uuid4().hex[:8]}"
subprocess.run(
[
"docker", "run", "--rm", "-d",
"--name", name,
"-e", "POSTGRES_PASSWORD=postgres",
"-e", "POSTGRES_DB=target_revenue_test",
"-p", "127.0.0.1::5432",
"postgres:16-alpine",
],
check=True, capture_output=True,
)
try:
port_out = subprocess.run(
["docker", "port", name, "5432/tcp"], check=True, capture_output=True, text=True
).stdout.strip()
host_port = port_out.split(":")[-1]
dsn = f"host=127.0.0.1 port={host_port} dbname=target_revenue_test user=postgres password=postgres"
for _ in range(60):
try:
with psycopg.connect(dsn, connect_timeout=1):
break
except psycopg.OperationalError:
time.sleep(0.5)
else:
raise RuntimeError("postgres container did not become ready in time")
with psycopg.connect(dsn) as conn:
for migration in MIGRATIONS:
conn.execute(migration.read_text(encoding="utf-8"))
conn.commit()
token = "test-token-onboard"
conn.execute(
"INSERT INTO licensors (token, licensor_id) VALUES (%s, %s)",
(token, "onboard-corp"),
)
conn.commit()
app_dsn = (
f"host=127.0.0.1 port={host_port} dbname=target_revenue_test "
f"user=trf_app password=changeme-in-deployment"
)
yield {"app_dsn": app_dsn, "token": token}
finally:
subprocess.run(["docker", "stop", name], capture_output=True)
@pytest.fixture()
def live_server(pg_container, monkeypatch):
monkeypatch.setenv("TRF_DATABASE_URL", pg_container["app_dsn"])
monkeypatch.setenv("TRF_SIGNING_KEY_HEX", "22" * 32)
from target_revenue.service import app as app_module
if hasattr(app_module.app.state, "pool"):
app_module.app.state.pool.close()
del app_module.app.state.pool
if hasattr(app_module.app.state, "signing_key"):
del app_module.app.state.signing_key
config = uvicorn.Config(app_module.app, host="127.0.0.1", port=0, log_level="warning")
server = uvicorn.Server(config)
thread = threading.Thread(target=server.run, daemon=True)
thread.start()
for _ in range(100):
if getattr(server, "started", False):
break
time.sleep(0.05)
else:
raise RuntimeError("uvicorn server did not start in time")
port = server.servers[0].sockets[0].getsockname()[1]
try:
yield f"http://127.0.0.1:{port}"
finally:
server.should_exit = True
thread.join(timeout=5)
def test_onboard_cli_registers_phase_and_appends_entry_against_live_server(
live_server, pg_container, tmp_path, monkeypatch, capsys
):
import importlib.util
spec = importlib.util.spec_from_file_location("trf_onboard", REPO_ROOT / "scripts" / "trf_onboard.py")
trf_onboard = importlib.util.module_from_spec(spec)
spec.loader.exec_module(trf_onboard)
manifest = golden_manifest()
manifest["phase"]["id"] = manifest["phase"]["id"] + "-onboard-" + uuid.uuid4().hex[:6]
manifest_path = tmp_path / "manifest.json"
manifest_path.write_text(json.dumps(manifest), encoding="utf-8")
monkeypatch.setenv("TRF_ONBOARD_TEST_TOKEN", pg_container["token"])
trf_onboard.main(
[
"register-phase",
"--url", live_server,
"--token-env", "TRF_ONBOARD_TEST_TOKEN",
"--manifest", str(manifest_path),
]
)
entry = {
"id": "trsl:entry:onboardcli0001",
"phase": manifest["phase"]["id"],
"type": "development-credit",
"amount": 1000,
"currency": "USD",
"recognized_at": "2026-08-01T00:00:00Z",
"evidence_reference": "confidential:evidence:onboardcli0001",
"extension": {"id": "trsl:extension:development-license", "version": "1.0"},
}
entry_path = tmp_path / "entry.json"
entry_path.write_text(json.dumps(entry), encoding="utf-8")
trf_onboard.main(
[
"append-entry",
"--url", live_server,
"--token-env", "TRF_ONBOARD_TEST_TOKEN",
"--phase-id", manifest["phase"]["id"],
"--entry", str(entry_path),
]
)
capsys.readouterr() # discard register-phase/append-entry output
trf_onboard.main(["status", "--url", live_server, "--phase-id", manifest["phase"]["id"]])
status = json.loads(capsys.readouterr().out)
assert status["facts"]["cumulative_development_credit"] == 1000

87
tests/test_trf_onboard.py Normal file
View file

@ -0,0 +1,87 @@
"""Pure, offline tests for scripts/trf_onboard.py (WP-0006-T07).
No network call is made in these tests they verify the offline
pre-flight validation short-circuits before any HTTP attempt, and that a
missing token env var fails loudly rather than silently sending an
unauthenticated request.
"""
from __future__ import annotations
import importlib.util
import json
import sys
from pathlib import Path
import pytest
from conftest import golden_manifest
REPO_ROOT = Path(__file__).resolve().parents[1]
_SPEC = importlib.util.spec_from_file_location("trf_onboard", REPO_ROOT / "scripts" / "trf_onboard.py")
trf_onboard = importlib.util.module_from_spec(_SPEC)
_SPEC.loader.exec_module(trf_onboard)
def test_validate_accepts_golden_manifest(tmp_path, capsys):
manifest_path = tmp_path / "manifest.json"
manifest_path.write_text(json.dumps(golden_manifest()), encoding="utf-8")
trf_onboard.main(["validate", str(manifest_path)])
assert "OK" in capsys.readouterr().out
def test_validate_rejects_broken_manifest(tmp_path):
broken = golden_manifest()
del broken["phase"]["initial_target"]
manifest_path = tmp_path / "manifest.json"
manifest_path.write_text(json.dumps(broken), encoding="utf-8")
with pytest.raises(SystemExit):
trf_onboard.main(["validate", str(manifest_path)])
def test_register_phase_rejects_offline_before_any_network_call(tmp_path, monkeypatch):
"""A non-conformant manifest must be caught by the offline check —
_request must never be called at all."""
broken = golden_manifest()
del broken["phase"]["initial_target"]
manifest_path = tmp_path / "manifest.json"
manifest_path.write_text(json.dumps(broken), encoding="utf-8")
def _boom(*args, **kwargs):
raise AssertionError("no network call should have been made")
monkeypatch.setattr(trf_onboard, "_request", _boom)
monkeypatch.setenv("TRF_TEST_TOKEN", "irrelevant")
with pytest.raises(SystemExit):
trf_onboard.main(
[
"register-phase",
"--url", "http://example.invalid",
"--token-env", "TRF_TEST_TOKEN",
"--manifest", str(manifest_path),
]
)
def test_register_phase_requires_token_env_to_be_set(tmp_path, monkeypatch):
manifest_path = tmp_path / "manifest.json"
manifest_path.write_text(json.dumps(golden_manifest()), encoding="utf-8")
monkeypatch.delenv("TRF_TEST_TOKEN_UNSET", raising=False)
def _boom(*args, **kwargs):
raise AssertionError("no network call should have been made without a token")
monkeypatch.setattr(trf_onboard, "_request", _boom)
with pytest.raises(SystemExit, match="TRF_TEST_TOKEN_UNSET"):
trf_onboard.main(
[
"register-phase",
"--url", "http://example.invalid",
"--token-env", "TRF_TEST_TOKEN_UNSET",
"--manifest", str(manifest_path),
]
)

View file

@ -273,7 +273,7 @@ passing under the Docker-gated suite; no stray containers left running.
```task
id: TREV-WP-0006-T07
status: todo
status: done
priority: medium
state_hub_task_id: "44c5846e-1f37-41fe-8ac0-fc7d88628e9f"
```
@ -287,6 +287,32 @@ Trust Service (the registration call). Coordinate with
the actual first repos — this task defines the mechanism, not the pilot
selection.
**Result:** `specs/TrustServiceOnboarding.md` defines the mechanism: a
Phase Manifest file is committed to the declaring repo (durable,
git-history-backed, independently foldable forever) *and* registered with
the hosted service (the multi-tenant, publicly-servable copy); once
registered, the Ledger's live authoritative copy is the hosted service
only, not a second competing file — a repo may keep periodic exports for
its own offline re-verification, but those are copies, not a source of
truth. Licensor token bootstrapping is explicitly out of this mechanism's
scope (a WP-0008-T01 governance action). `scripts/trf_onboard.py`
implements it as a dependency-light CLI (stdlib `urllib` + the existing
`target_revenue.validation`, no FastAPI/psycopg required to onboard a
Phase) with four subcommands: `validate` (offline only, no network),
`register-phase` (validates offline before ever attempting the network
call), `append-entry`, and `status` (public, no token). The Licensor API
token is read only from an environment variable named on the command
line, never accepted as a literal argument or committed. `tests/test_trf_onboard.py`
(4 tests, no network, no Docker) proves both invalid-manifest and
missing-token-env cases fail before any HTTP attempt is made — asserted by
monkeypatching the request function to raise if called at all.
`tests/test_onboarding_hosted.py` (1 Docker-gated test) is the one test in
the suite that runs an actual `uvicorn` server on a real socket (rather
than FastAPI's in-process TestClient, since the CLI genuinely speaks HTTP)
and drives the full register → append → status round trip through the CLI
exactly as an external repo would invoke it. Full suite: 49 passing
offline, 71 passing with Docker; no stray containers left running.
## Conformance test suite at hosted scale
```task