docs: define hub-core absorption plan
Some checks failed
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / pytest-smoke (push) Failing after 3s

This commit is contained in:
tegwick 2026-08-21 10:57:13 +02:00
parent 5802d6f799
commit 4a9df801c3
7 changed files with 216 additions and 25 deletions

View file

@ -9,6 +9,7 @@ This directory is the specification map for Core Hub. The specs are intentionall
- [API v2 Compatibility](api-v2-compatibility.md) - Inter-Hub compatibility surface to preserve during transition
- [Inter-Hub Legacy Inventory](interhub-legacy-inventory.md) - initial route/data/consumer inventory for migration
- [Runtime Absorption Inventory](runtime-absorption-inventory.md) - current routes, data, consumers, and ownership that hub-core absorption must preserve
- [Runtime Absorption Plan](runtime-absorption-plan.md) - ordered dual-run route groups, migration gates, traffic control, and rollback
- [Contract Governance](contract-governance.md) - how contract changes are reviewed and recorded
- [Data Model](data-model.md) - initial entity model and migration posture
- [Event and Registry Model](event-and-registry-model.md) - event catalogs, manifests, widgets, and capability registries

View file

@ -0,0 +1,168 @@
# Core Hub to hub-core absorption plan
**Status:** recorded for `CORE-WP-0010-T02`; joint artifact for `HUB-WP-0004-T06`
**Reviewed:** 2026-08-21
**Strategy:** route-group migration with Core Hub retained as rollback; no
client dual-write and no big-bang cutover
This plan turns the inventory in `runtime-absorption-inventory.md` into ordered,
testable migration slices. Hub-core owns the surviving runtime selected by its
ADR-0001. Core Hub remains the public `/api/v2` authority until each route group
passes its own data, contract, consumer, and rollback gates.
## Decisions
1. **One target runtime.** Hub-core ships the importable wheel and primary OCI
image. Core Hub is a temporary source and rollback runtime, not a permanent
host for hub-core.
2. **Internal candidate first.** A hub-core candidate deployment receives no
public traffic until it has a published source revision, immutable image
digest, durable PostgreSQL backend, migrations, compatible authentication,
and green readiness.
3. **Route groups, not percentages.** Traffic moves by coherent API group.
Random percentage canaries would send related bootstrap calls to different
authorities and are not permitted.
4. **One writer per route group.** Read-only requests may be mirrored for
comparison. Production clients never dual-write. Write cutover uses a short
drain, final delta replay, route switch, and one active writer.
5. **Isolated metadata during dual-run.** Core Hub keeps the `core_hub` schema;
hub-core uses its own migration lineage and schema. Cross-importing either
service's SQLAlchemy metadata is prohibited.
6. **Stable identity and safe key migration.** Durable IDs are preserved.
API-key prefixes, hashes, scopes, and status may move; raw bearer tokens or
one-time full keys must never enter migration bundles or evidence.
7. **Archive last.** Core Hub stays deployable until the final stabilization
gate, reverse-delta requirement, and residual ownership record are closed.
## Entry gates
Absorption implementation does not begin until all of these are evidenced:
- the completed `HUB-WP-0004-T01``T05` work is committed and pushed;
- a hub-core wheel and OCI image identify the same revision and version;
- `rapp-core-hub` can deploy a second, cluster-internal candidate without
changing `hub.coulomb.social`;
- hub-core has a production PostgreSQL `PortStore`/compatibility store and
fails readiness when durable storage is unavailable;
- the existing operator token and copied API-key hashes authenticate through
the candidate without exposing credential values;
- Core Hub export, hub-core import, row-count, canonical-digest, and delta
replay commands are repeatable and record non-secret migration metadata;
- the hub-core conformance profile and Core Hub compatibility tests pass
against the candidate.
The current in-memory hub-core backend is a conformance implementation, not an
absorption target. Its deliberate production-readiness failure is a useful
gate and must not be bypassed.
## Ordered slices
| Slice | Surface | Move condition | Consumer gate |
| --- | --- | --- | --- |
| 0 — candidate foundation | image, API/MCP processes, migrations, durable store, auth adapter, health/readiness | All entry gates above; no public route | hub-core conformance plus Core Hub contract suite |
| 1 — stateless compatibility | widget/event/annotation/policy catalogs; empty annotation, requirement, decision, deployment, and outcome collections; compatibility OpenAPI/docs | Response status, headers, and canonical JSON match; unprefixed OpenAPI aliases retained | unauthenticated catalog and OpenAPI probes |
| 2 — protected bootstrap | hubs, capability manifests and activation, API consumers/key issuance, widgets, hub registry | IDs/FKs preserved; source and target counts/digests match; copied key hashes authenticate; isolated create/read fixture passes | complete ops-hub bootstrap/gate probe |
| 3 — interaction evidence | interaction-event append/list and mapping to `port.events.interaction` | Historical IDs/timestamps retained; adapter mapping is reversible; append/readback fixture passes | activity-core resolver and evidence-sink probe |
| 4 — operator surface | `/console`, readiness summary, compatibility redirects and remaining aliases | Console reads target projections; no source-only API calls remain | operator CLI, protected console, visual smoke |
| 5 — public authority | all `/api/v2`, health/readiness, API/MCP entrypoints | Earlier slices stable; rollback and residual-owner evidence current | ops-hub and activity-core gates together |
Slice 2 is intentionally atomic at the traffic boundary. The ops-hub bootstrap
creates records whose identifiers cross hubs, manifests, consumers, keys, and
widgets; splitting those calls across two authorities would manufacture
referential drift. Its code and data migration may still land in smaller
reviewable commits before the route group switches.
## Compatibility adapters
Hub-core's named ports are not themselves the legacy `/api/v2` contract. The
target needs an explicit compatibility composition layer:
- hub and manifest resources map to `port.registry` while preserving legacy
IDs, body fields, activation behavior, pagination envelope, and hub-registry
projection;
- interaction events map to `port.events.interaction` with a stable
correlation ID, original event ID, widget reference, view context, event
type, metadata, and occurrence/recording timestamps;
- widgets and API consumers/keys remain compatibility resources until a named
replacement port is accepted; they must not be forced into registry records;
- health/readiness retain the response shape expected by deployed Core Hub
smoke tooling until consumers adopt the hub-core-native shape;
- `/api/v2/openapi.json`, YAML/docs discovery, and required unprefixed aliases
remain versioned compatibility artifacts through final cutover.
## Data movement and comparison
For each durable slice:
1. Export source records in dependency order with a schema version, source
revision, high-water mark, and bundle SHA-256.
2. Validate without writing; reject unknown fields, missing parents, raw secret
shapes, duplicate identities, or a lower target schema version.
3. Upsert into the target by stable ID. Re-running the same bundle must be
idempotent and recorded as such.
4. Compare table/resource counts, sorted identity sets, and canonical row
digests. Timestamp formatting and JSON key order are normalized before
hashing; semantic field differences are not ignored.
5. Mirror only safe GET requests and compare status plus canonical response.
Exercise writes with isolated migration fixtures, never by duplicating a
production client request.
6. At route cutover, drain writes for that group, capture the final high-water
delta, import/compare it, switch the route group, and enable target writes.
Migration evidence may contain IDs, counts, hashes, versions, timestamps, and
pass/fail results. It must not contain raw API keys, bearer tokens, database
credentials, Authorization headers, or copied Kubernetes Secrets.
## Traffic controls and rollback
`rapp-core-hub` owns one logical switch per slice with the states `source`,
`shadow-read`, and `target`. The concrete gateway or Helm key names are a
deployment implementation detail, but every rendered release must reveal the
selected state as non-secret evidence.
Rollback remains possible while a slice is target-owned:
1. stop new target writes for the affected route group;
2. export and verify the target-only delta by stable ID;
3. replay that delta to Core Hub when the old source must become writable;
4. compare counts/digests and run the consumer gate;
5. switch the whole route group back to `source`.
If reverse replay is not implemented for a write slice, rollback is limited to
the period before target writes are enabled. That limitation blocks public
cutover and must be visible in the evidence report.
## Required evidence per slice
- hub-core source revision, package version, and OCI digest;
- Core Hub source revision and deployed Helm revision;
- source/target schema versions and migration bundle hash;
- before/after counts, identity-set comparison, and canonical digest result;
- compatibility/OpenAPI diff result;
- auth positive and negative checks without credential disclosure;
- named consumer smoke reports and correlation IDs;
- selected route state, switch time, observer, and rollback result or reason it
was not invoked.
Any schema mismatch, unexplained count/digest drift, secret-shaped evidence,
auth broadening, consumer failure, or non-green readiness stops the slice and
keeps public authority on Core Hub.
## Ownership
| Owner | Responsibility |
| --- | --- |
| hub-core | compatibility modules, durable store/migrations, import/delta/reverse-delta, conformance |
| core-hub | source export, legacy fixtures/OpenAPI, canonical response comparison, rollback service |
| rapp-core-hub | candidate deployment, immutable image pin, route switches, rollout/rollback evidence |
| railiance-platform / rapp-postgres | PostgreSQL, credential delivery, backup/restore readiness |
| ops-hub | bootstrap and registry consumer gate |
| activity-core | interaction resolver, append, and readback gate |
| operator | approval for Slice 5 public-authority cutover and later repository archive |
## Next executable work
After hub-core publishes its completed T01T05 revision, start Slice 0. The
first implementation commit should add the durable PostgreSQL boundary and
legacy auth/health compatibility in hub-core, plus a candidate deployment mode
in `rapp-core-hub`. It must not change public DNS or ingress.