feat: publish repository classification projections
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a053ff-1d6f-7fe2-ac1c-a6eb40a42a0c
This commit is contained in:
parent
90e8d78ad3
commit
54271a2261
8 changed files with 1327 additions and 1 deletions
64
docs/evidence/RMGR-WP-0013-live-conformance-2026-09-01.md
Normal file
64
docs/evidence/RMGR-WP-0013-live-conformance-2026-09-01.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# RMGR-WP-0013 live publisher conformance — 2026-09-01
|
||||
|
||||
## Scope
|
||||
|
||||
This evidence covers the Repo Manager classification publisher and hub-core's
|
||||
frozen `helixforge.repository-classification-projection` 1.0.0 consumer. It is
|
||||
a live source/conformance proof from the workstation checkout fleet, not a
|
||||
production deployment claim.
|
||||
|
||||
## Registrar bootstrap
|
||||
|
||||
- State Hub source: `primary` / `railiance01`
|
||||
- Discovery root: `/home/worsch` (operator-local, not published)
|
||||
- Registered classified repositories: **123**
|
||||
- Locally classified but unregistered checkouts: **2**
|
||||
(`snuggles-inventor`, `wise-validator`), reported as warnings and omitted
|
||||
- Stable IDs: existing State Hub registrar UUIDs; none invented
|
||||
- Bulk `/repos/` was not used because it timed out after 180 seconds
|
||||
- Successful bounded import: four workers, 15-second request timeout, two
|
||||
transient retries
|
||||
|
||||
The generated private registry was written only to `/tmp` and was not committed.
|
||||
It contained host paths for local observation; those paths did not enter the
|
||||
published projection.
|
||||
|
||||
## End-to-end transfer
|
||||
|
||||
The publisher served the live registry with a page size of 25. Hub-core fetched
|
||||
five HMAC-bound pages through its new HTTP `port.repo` client and accepted the
|
||||
complete transfer atomically.
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "accepted",
|
||||
"snapshot_id": "c22faf46794ee9288102d6f0f98b04372ebfaa2c41017edf11c6101f157ec654",
|
||||
"repository_count": 123,
|
||||
"projection_status": "current",
|
||||
"content_hash": "a3a1d584c8245b6c339bd672551d0960bf2b0b133e259bec7e884fe302cfe14c",
|
||||
"source_revision": "b28a45e7c9592f6aecdc4537b463333545ba6f1c68746d6a5e904cff4d654008"
|
||||
}
|
||||
```
|
||||
|
||||
Repo Manager read classification from each repository's authoritative file and
|
||||
published only contract fields. Hub-core derived its own navigation generation;
|
||||
no database or private persistence model was shared.
|
||||
|
||||
## Verification
|
||||
|
||||
- Repo Manager: **156 passed**; Ruff clean
|
||||
- Hub-core: **107 passed**
|
||||
- Focused publisher tests cover duplicate identities, deterministic UUID order,
|
||||
Git/source provenance, multi-page transfer, cursor tampering, invalid source
|
||||
diagnostics, bearer authentication, readiness, and primary registrar import
|
||||
- Hub-core focused tests cover cursor/token HTTP transport plus all frozen
|
||||
repository-navigation contract and ingestion behaviors
|
||||
|
||||
## Remaining production gate
|
||||
|
||||
The code and cross-repository transport are ready. Production still needs an
|
||||
explicit placement/network decision because hub-core runs in the public Core
|
||||
Hub cluster while the authoritative checkout registry is host-local to the
|
||||
Repo Manager worker. No broad host-path mount or implicit State Hub dependency
|
||||
was introduced to hide that boundary. `RMGR-WP-0013-T05` remains waiting for
|
||||
that deployment handoff; `HUB-WP-0006-T06` must not switch traffic before it.
|
||||
113
docs/repository-classification-publisher.md
Normal file
113
docs/repository-classification-publisher.md
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
# Repository classification publisher
|
||||
|
||||
Repo Manager publishes the frozen
|
||||
`helixforge.repository-classification-projection` 1.0.0 envelope at:
|
||||
|
||||
```text
|
||||
GET /ports/repositories/classifications?cursor=<opaque>
|
||||
```
|
||||
|
||||
The route is read-only. It contains registrar UUID, slug, lifecycle,
|
||||
classification, Git revision, source fingerprint, and observation time. Local
|
||||
paths, remotes, work records, source bodies, and credentials never enter the
|
||||
envelope.
|
||||
|
||||
## Private operator registry
|
||||
|
||||
Publication joins repository-owned `.repo-classification.yaml` files to a
|
||||
private operator registry. The registry is local runtime state because checkout
|
||||
paths are host-specific; do not commit it. Its schema is:
|
||||
|
||||
```yaml
|
||||
schema: repo-manager.repository-registry.v1
|
||||
repositories:
|
||||
- repository_id: a9d105c6-10fa-4cf9-8bc1-e248476698d3
|
||||
slug: repo-manager
|
||||
lifecycle: active
|
||||
path: /home/operator/repo-manager
|
||||
```
|
||||
|
||||
Bootstrap current stable identities from the retiring primary State Hub:
|
||||
|
||||
```bash
|
||||
rmgr publisher import-registry \
|
||||
--root /home/operator \
|
||||
--api-base http://127.0.0.1:8000 \
|
||||
--output ~/.repo-manager/repository-registry.yaml
|
||||
```
|
||||
|
||||
The import verifies `instance_role=primary`, discovers only classified Git
|
||||
working copies below the selected root, and uses bounded direct identity reads.
|
||||
It does not use State Hub's expensive bulk repository projection and does not
|
||||
trust State Hub host paths. Transient reads retry; locally classified but
|
||||
unregistered checkouts are retained as warnings and omitted rather than being
|
||||
assigned invented identities. Any other failure prevents replacement of the
|
||||
registry file.
|
||||
|
||||
After repository-registry authority moves fully into Repo Manager, normal
|
||||
governed registration maintains this same private schema and the bootstrap
|
||||
command can be retired. The publisher contract does not change.
|
||||
|
||||
## Run
|
||||
|
||||
Set runtime configuration through the environment:
|
||||
|
||||
```text
|
||||
REPO_MANAGER_REGISTRY_PATH=~/.repo-manager/repository-registry.yaml
|
||||
REPO_MANAGER_CURSOR_SECRET=<at-least-32-byte-secret>
|
||||
REPO_MANAGER_API_TOKEN=<optional-bearer-token>
|
||||
REPO_MANAGER_PROJECTION_PAGE_SIZE=100
|
||||
REPO_MANAGER_PROJECTION_MAX_SNAPSHOTS=4
|
||||
```
|
||||
|
||||
Secret values belong in the platform credential path and must not be committed
|
||||
or logged. Start the API with:
|
||||
|
||||
```bash
|
||||
rmgr publisher api --host 0.0.0.0 --port 8020
|
||||
```
|
||||
|
||||
`GET /healthz` is process liveness. `GET /readyz` verifies that configuration
|
||||
and the registry are usable. When `REPO_MANAGER_API_TOKEN` is set, the
|
||||
projection route requires the matching bearer token.
|
||||
|
||||
## Snapshot and failure semantics
|
||||
|
||||
A first-page request observes the complete registry into an immutable in-memory
|
||||
snapshot. Repositories are ordered by registrar UUID. Subsequent page cursors
|
||||
carry snapshot ID, offset, and page size protected by HMAC-SHA256. Only a small
|
||||
bounded set of in-flight snapshots is retained; an invalid, tampered, or
|
||||
expired cursor returns `409`.
|
||||
|
||||
Missing checkouts, invalid classifications, or unavailable Git revisions are
|
||||
emitted as bounded error diagnostics. Hub-core rejects any such transfer and
|
||||
keeps the last accepted generation, so a partial observation never becomes the
|
||||
active navigation projection.
|
||||
|
||||
## Hub-core client
|
||||
|
||||
Configure the hub-core runtime with:
|
||||
|
||||
```text
|
||||
HUB_CORE_REPO_MANAGER_BASE_URL=http://repo-manager:8020
|
||||
HUB_CORE_REPO_MANAGER_API_TOKEN=<same-optional-bearer-token>
|
||||
HUB_CORE_REPO_MANAGER_TIMEOUT_SECONDS=10
|
||||
HUB_CORE_REPO_PROJECTION_REFRESH_SECONDS=300
|
||||
```
|
||||
|
||||
Hub-core refreshes once at startup and then on the configured interval. A
|
||||
failed refresh marks the dependency stale while preserving the last accepted
|
||||
generation. Setting the refresh interval to `0` disables the background loop.
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
make test
|
||||
make publisher-snapshot \
|
||||
REGISTRY=~/.repo-manager/repository-registry.yaml \
|
||||
CURSOR_SECRET='<runtime-secret>'
|
||||
```
|
||||
|
||||
RMGR-WP-0013's live conformance used five pages of 25 rows. Hub-core accepted
|
||||
all 123 registered classifications atomically and produced one current
|
||||
generation; no State Hub classification table was read by the publisher.
|
||||
Loading…
Add table
Add a link
Reference in a new issue