hub-core/docs/repository-navigation-contract.md
tegwick a701dc89cf
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / pytest-smoke (push) Failing after 1s
feat: freeze repository navigation contract
Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a0230c-b06c-7641-808a-e191b6d1da49
2026-08-22 00:27:44 +02:00

4 KiB

Repository navigation contract 1.0.0

Status: frozen for HUB-WP-0006 T02 on 2026-08-22.

The packaged contract is helixforge.repository-navigation version 1.0.0. It consumes only helixforge.repository-classification-projection version 1.0.0, representing RMGR-CONTRACT-CLASSIFICATION-0001 version 1.0. Unsupported versions are rejected; hub-core does not coerce them.

Authority and identity

Repository .repo-classification.yaml files remain authoritative. Repo Manager validates and publishes them. Hub-core owns only rebuildable navigation rows, facets, query cursors, diagnostics, and temporary compatibility aliases.

Every change carries the registrar's stable repository_id UUID. Slugs may change and are display/routing labels, not identity. The Repo Manager adapter adds that UUID when converting its local observation into this envelope. locations, repo_root, work-record bodies, credentials, and other host-local or authority-bearing data are forbidden by the closed schemas.

Pages, snapshots, and changes

All pages in a transfer have the same snapshot_id, mode, generated_at, source_revision, and total_repository_count. Entries are ordered by repository_id; duplicate IDs in one transfer are invalid.

  • The first page has a null page_cursor.
  • next_cursor is null exactly when final_page is true.
  • Cursors are opaque, signed or equivalently integrity-protected, bound to the snapshot and normalized filter set, and never parsed by clients.
  • A full snapshot consists of upserts. Absence means deletion only after every page validates and the final page is accepted.
  • An incremental snapshot contains explicit idempotent upsert/delete changes. A change is newer only by its source observation/revision semantics; duplicate delivery has no effect.
  • total_repository_count is required for a full snapshot and may be null for incremental delivery.

Activation is atomic. Hub-core builds into a new generation, derives all six facet kinds, computes SHA-256 over canonical JSON repository rows ordered by UUID, and swaps the active generation only after the complete transfer passes schema and semantic validation. A failed full rebuild never deletes or partly replaces the last accepted generation.

Failure and diagnostics

An error diagnostic, malformed page, inconsistent snapshot metadata, unsupported version, duplicate identity, invalid controlled value, or upstream failure rejects that transfer. The last accepted projection remains readable as stale, includes a diagnostic and source_checked_at, and readiness reports the Repo Manager dependency as degraded. If no accepted generation exists, the query port returns 503. Successful acceptance restores current.

Provenance on every repository includes the Repo Manager URI, classification contract version, source fingerprint, observed time, and repository revision. The response also carries the source snapshot/revision and rebuild content hash. Diagnostics must not include secrets or unbounded source content.

Query semantics

Repositories are returned in ascending UUID order. Repeated values within one filter family are ORed; different filter families are ANDed. The six families are primary domain, secondary domain, category, capability tag, business stake, and business mechanic. An unknown value produces 400; an empty match is a successful empty page.

Response cursors are bound to both the active content hash and normalized filters. Reusing a cursor after a rebuild or with different filters returns 409 cursor_snapshot_mismatch, so a client restarts from the first page.

Legacy topic aliases

legacy_topic_aliases is temporary, read-only compatibility metadata. Each entry maps one State Hub topic UUID/slug to one derived facet and must name an expires_at time and residual_owner. It cannot create, update, or delete classification. Expired aliases return 410 until removed; there are no write operations in the navigation OpenAPI surface. STATE-WP-0079 owns alias expiry and the consumer route switch.