state-hub/workplans/STATE-WP-0085-repository-lineage-preserving-rename.md

609 lines
28 KiB
Markdown
Raw Normal View History

---
id: STATE-WP-0085
type: workplan
title: "Lineage-preserving repository rename workflow and adoption-plan generator"
domain: infotech
repo: state-hub
status: active
owner: codex
topic_slug: infotech
created: "2026-08-28"
updated: "2026-08-29"
reviewed_at: "2026-08-28"
reviewed_by: codex
reviewed_against_commit: "41d80429a77ffeb640f4b34091ebee5c91585373"
quality_dor: DoR-Ok
quality_dor_at: "2026-08-28"
quality_dor_by: codex
related:
- CUST-ADR-012
- STATE-WP-0083
- STATE-WP-0084
state_hub_workstream_id: "4f5661de-5922-55d6-bd02-5dcb00b13f73"
---
# Lineage-preserving repository rename workflow and adoption-plan generator
## Goal
Make a repository rename a governed identity migration instead of an
archive-and-register operation. State Hub must preserve the repository UUID,
workplan and task identities, progress, decisions, telemetry, SBOM history, and
coordination history while the canonical Forgejo name, clone URL, and local
paths change.
The capability must also generate a repository-native migration workplan that
the repository being renamed can adopt. `flex-auth` to `access-engine` is the
first intended adoption, but the implementation and template must be generic.
## Why this exists
Repository names are currently used as both mutable labels and identity keys.
The durable relationships are already mostly correct: workplans and token
events point to `managed_repos.id`. The public repository API, file bindings,
message routing, interface-change affected lists, identifier-alias provenance,
fabric projections, local paths, and forge URLs still contain slug-shaped
references.
State Hub has no supported repository rename operation. Registering
`access-engine` as a new repository would split the history of Forgejo
repository id `42` and State Hub repository id
`fda8ad85-a7d7-4055-8f21-902a533e59df`. Updating the database row manually
would preserve some foreign keys but leave slug-only surfaces, retries, and
failure recovery undefined.
`flex-auth` is part of the NetKingdom security stack. Its repository identity,
container publishing, deployment references, policy consumers, credentials,
fabric declarations, and State Hub routing cannot be cut over safely as an
unrecorded sequence of shell commands.
## Invariants
Every implementation and generated migration plan must preserve these facts:
1. A rename does not create a second `managed_repos` row.
2. The State Hub repository UUID is unchanged.
3. The Forgejo repository numeric ID and selected source commit are unchanged.
4. Existing workplan IDs, task IDs, hub UUIDs, progress events, decisions, and
token events are unchanged.
5. Existing work-record prefixes and filenames are not re-keyed merely because
the repository slug changes.
6. The old slug remains a durable alias. Historical records are not mass
rewritten to pretend the old name never existed.
7. Every phase is idempotent and resumable. A retry reports the achieved phase
rather than applying a second rename.
8. No old clone, alias, route, or rollback input is removed before verification
succeeds and the target repository's migration workplan authorises cleanup.
9. Forge redirects are compatibility evidence, not a substitute for updating
canonical remotes and consumers.
10. An unreadable forge repository, changed repository ID, changed head, slug
conflict, or incomplete projection stops before mutation.
## Scope
### In scope
- Durable forge identity and repository-slug aliases in State Hub.
- A preflight, apply, verify, resume, and rollback contract.
- Alias-aware lookup and routing for State Hub-owned surfaces.
- CLI/API surfaces suitable for later HelixForge orchestration.
- A generator for a target-repository rename workplan.
- Operator documentation, events, metrics, and failure evidence.
- A non-mutating `flex-auth` to `access-engine` rehearsal and generated
repository workplan as the first adoption handoff.
### Out of scope
- Executing the live `flex-auth` Forgejo rename in this State Hub workplan.
- Deciding whether the product binary, Go module, `FLEX_AUTH_*` environment
variables, Kubernetes namespace, Helm releases, service DNS names, container
package, or policy vocabulary also become `access-engine`. The generated
target workplan must make those explicit decisions.
- Rewriting historical prose, audit records, message bodies, or raw telemetry.
- A general Forgejo repository transfer between owners. The model may leave
room for transfer, but owner transfer requires a separate reviewed contract.
- Direct secret retrieval or embedding a Forgejo credential in State Hub
records, logs, templates, or command arguments.
## Ownership and execution boundary
- **State Hub** owns repository identity, aliases, work-record continuity,
routing compatibility, audit evidence, and verification.
- **Forgejo** remains authoritative for the Git repository and its immutable
repository ID.
- **HelixForge orchestration** may later coordinate the Forgejo operation,
State Hub phase transitions, and local-clone registration through the
contracts delivered here. It must not bypass them with database writes.
- **The target repository** owns the generated migration workplan, consumer and
deployment consequences, execution evidence, and final cleanup decision.
Capability implementation is Yellow-lane work. A live forge rename, canonical
route switch, old-alias retirement, or old-checkout removal is Red-lane work and
requires an explicit operator confirmation recorded by the target workplan.
## Target lifecycle
```text
draft
-> preflighted
-> forge-renamed
-> statehub-rebound
-> source-synced
-> consumers-verified
-> completed
Any achieved phase
-> retry same phase safely
-> rollback-preflight
-> rolled-back (when the old forge slug is still available)
```
The operation record reports incomplete phases as recoverable state. It must
never collapse "Forgejo renamed, State Hub not rebound" into a generic error
that leaves the operator guessing which action already happened.
## Define the identity and failure contract
```task
id: STATE-WP-0085-T01
status: done
priority: high
state_hub_task_id: "dbac0fdf-042e-51bf-937e-59d15e34cb14"
```
Record the architecture decision for mutable repository coordinates over an
immutable repository identity. Define:
- State Hub repository UUID versus canonical slug and aliases;
- Forgejo provider, owner, immutable repository ID, canonical name, and URL;
- expected source commit and default branch at preflight;
- phase transitions, retry semantics, and rollback limits;
- which slug-shaped fields are mutable projections, immutable historical
evidence, external-source projections, or live routing keys;
- the boundary between a repository rename and a product/runtime rename.
Inventory every State Hub model, API, CLI, event, dashboard query, edge outbox
shape, and external projection that carries a repository slug. Classify each as
foreign-key anchored, alias-resolved, reprojected, historically retained, or
requiring a target-repo handoff.
Acceptance:
- the ADR contains the invariant and failure-state tables;
- every current slug-bearing surface has one named treatment and owner;
- an operation interrupted after each phase has a documented safe next action;
- owner transfer and product/runtime rename are explicitly outside this
contract rather than silently half-supported.
Result (2026-08-28): accepted `STATE-ADR-001` in
`docs/adr/ADR-001-repository-rename-identity-contract.md`. It defines immutable
State Hub and Forge identities, protected slug aliases, compare-and-set phase
semantics, retry and rollback limits, twelve named failure states, recovery from
every phase, and the treatment/owner inventory for persistence, REST, CLI, MCP,
consistency projections, dashboard queries, events, edge relay state, Forgejo,
local clones, and external consumers. Owner transfer and product/runtime rename
are explicit target-workplan boundaries.
## Add durable forge identity, aliases, and rename operations
```task
id: STATE-WP-0085-T02
status: done
priority: high
state_hub_task_id: "c9e6c8c5-c254-5f4d-982c-c161f6ed6d29"
```
Add the persistence needed to distinguish a repository from its current name.
The exact normalization may be decided in T01, but it must represent:
- a forge provider/instance, owner, and immutable repository ID attached to the
existing managed repository;
- one canonical slug plus durable prior-slug aliases;
- a rename operation with old/new coordinates, expected repository IDs and
commits, current phase, actor, timestamps, evidence, and error state;
- uniqueness rules preventing one live slug or forge identity from resolving
to two repository UUIDs.
Backfill must be non-destructive. Repositories whose forge identity cannot be
proven remain unverified and cannot be renamed; absence is not guessed from the
slug or initial Git commit.
Acceptance:
- migration upgrade and downgrade tests pass;
- the same old slug cannot be claimed by a different live repository while it
is a protected alias;
- `flex-auth` can be preflighted as State Hub UUID
`fda8ad85-a7d7-4055-8f21-902a533e59df` and Forgejo repository ID `42` without
changing either system;
- no migration rewrites workplan, task, progress, decision, token, or SBOM
identities.
Result (2026-08-28): migration `f3c4d5e6a7b8` adds an explicitly unverified or
verified Forge identity per managed-repository UUID, a global canonical/alias
slug registry, and a phased rename-operation journal. Composite foreign keys
prevent operations for unverified or mismatched repository identities; partial
unique indexes reserve one canonical slug, one active operation per repository,
and one active target slug. Database history guards prevent verified Forge
identity, protected slug, and operation-assertion rewrites or deletion while
leaving phase/evidence/error progress updateable. Existing repositories are
backfilled only with an unverified marker and their current canonical slug;
Forge coordinates are not guessed. Both API and direct classification
registration create the same initial records. PostgreSQL tests prove protected
alias conflicts, unique Forge identity, active-operation locks, a non-mutating
`flex-auth`/Forgejo-id-42 preflight representation with all existing identities
preserved, isolated migration upgrade/downgrade, and full-chain migration. The
full Python suite passes (765 tests).
## Implement preflight, apply, resume, verify, and rollback APIs
```task
id: STATE-WP-0085-T03
status: done
priority: high
state_hub_task_id: "2cb4d7ac-0192-5d46-aa87-7412e99b0cef"
```
Provide repository-ID-addressed APIs for the lifecycle. Slug-addressed
convenience routes may resolve through aliases, but the mutating operation must
bind to the stable repository UUID and expected Forgejo ID.
Preflight reports, without mutation:
- current State Hub and Forgejo identity;
- old/new slug availability;
- local and registered host paths;
- source commit and projection readability;
- workplan/task/telemetry baselines and active work;
- affected messages, bindings, interface changes, SBOM data, services,
capability entries, fabric nodes, and external handoffs;
- queued edge writes or active operations that make cutover unsafe;
- exact proposed mutations and retained historical values.
Apply performs only the requested phase and uses compare-and-set expectations.
Verify compares immutable IDs, commits, counts, relationship checksums, routes,
and projection results. Rollback first proves the old slug is available and
reports what cannot be reversed automatically.
Acceptance:
- dry-run is byte-for-byte free of persistent changes;
- mutation requires a successful, unexpired preflight token or equivalent
compare-and-set evidence plus explicit confirmation;
- repeating any completed phase is a no-op with the same operation ID;
- conflicting slug, wrong Forgejo ID, moved head, unreadable forge, or stale
preflight fails closed;
- API tests interrupt and resume after every phase;
- the managed-repository UUID is asserted unchanged throughout.
Implemented 2026-08-29. UUID-addressed endpoints now verify immutable Forgejo
identity, produce HMAC-signed non-mutating preflights, create discoverable
operation journals, apply one compare-and-set phase at a time, verify baseline
identity continuity, and preflight/apply bounded rollback. The Forge adapter
uses authenticated absence proofs and a separate rename credential; API tests
replace it with an in-memory boundary and cannot touch live Forgejo. Resume
recognizes a Forge rename that committed before its journal phase, completed
phase replays are no-ops with the same operation UUID, and rollback recognizes
the corresponding unrecorded-Forge state. Baseline IDs must remain present
while append-only telemetry may grow during the operation. The follow-on
migration permits a protected alias to become canonical only for its own
operation in `rollback-preflight`; all other alias mutation remains rejected.
Tests cover dry-run persistence, expiry and confirmation, occupied slugs,
queued edge writes, wrong Forge ID, moved head/stale evidence, unreadable Forge,
phase interruption/replay, telemetry append continuity, rollback, trigger
guarding, and full migration upgrade/downgrade. The full Python suite passes
(771 tests).
## Make State Hub reads and routing alias-aware
```task
id: STATE-WP-0085-T04
status: done
priority: high
state_hub_task_id: "dcf7102e-ed7a-503e-bf78-db2149f72453"
```
Resolve prior slugs to the canonical repository identity without falsifying
history. At minimum cover:
- repository lookup and dispatch;
- consistency registration and workplan file bindings;
- inbox reads and new-message routing;
- interface-change affected-repository queries;
- work-record identifier aliases;
- repository goals, capabilities, services, SBOM, and token summaries;
- dashboard and state-summary links;
- edge cache/outbox replay produced before or during cutover.
Historical messages keep their recorded sender and recipient. Queries through
the canonical slug include aliased history, and new messages use the canonical
slug. External-source projections such as `railiance-fabric` are reported as
handoffs and remain visibly stale until their owning source is updated and
reingested.
Acceptance:
- old and new slugs resolve to one repository UUID with canonical/alias status
visible in the response;
- old-slug history is visible from the new repository without rewriting stored
history;
- a queued old-slug write replays exactly once against the same repository;
- new bindings use the new slug while workplan and task UUIDs remain unchanged;
- external stale references are named rather than silently rewritten.
Implemented one protected-slug resolver and applied it across repository reads,
dispatch, consistency/path binding, inbox routing, interface changes,
work-record identifier migration, goals, capability and service catalogs, SBOM,
token summaries, Fabric projection reads, and dashboard links. Repository
responses expose requested/canonical slug, canonical-or-alias status, aliases,
and named stale external projections. Historical message and interface values
remain unchanged; new writes are canonicalized. An idempotent old-slug message
replay is proven to persist once, while workplan/task UUIDs and repository FKs
remain stable. Verification: 775 Python tests and the 70-page dashboard build
pass (the build retains one pre-existing `/docs/intakes` broken-link warning).
## Add the State Hub CLI and HelixForge orchestration contract
```task
id: STATE-WP-0085-T05
status: done
priority: high
state_hub_task_id: "d85da2ea-8037-5117-a41a-d400ca68bf10"
```
Expose a non-interactive, machine-readable CLI over the API lifecycle. The
final spelling follows existing State Hub CLI conventions; it must provide the
equivalent of:
```text
statehub repo rename preflight <old> <new> --json
statehub repo rename apply <operation-id> --phase <phase> --confirm
statehub repo rename status <operation-id> --json
statehub repo rename verify <operation-id> --json
statehub repo rename rollback <operation-id> --confirm
```
The contract must make it possible for HelixForge to coordinate the Forgejo
rename without receiving database access. Forgejo credentials follow the
credential-routing catalog and must never be accepted as a command-line value
that can appear in process listings.
Acceptance:
- JSON output is stable enough for orchestration and names the next safe action;
- human output distinguishes planned, achieved, failed, and rolled-back phases;
- every mutating command requires an operation ID and explicit confirmation;
- CLI retries preserve operation identity;
- no command logs credentials, authorization headers, or secret-bearing URLs.
Result (2026-08-29): added `statehub repo rename`
`preflight`/`start`/`apply`/`status`/`verify`/`rollback` commands over the
repository-ID-addressed lifecycle. The versioned
`state-hub.repository-rename-cli.v1` JSON envelope reports lifecycle state and
the next safe command; human output distinguishes planned, achieved, failed,
and rolled-back operations. Preflight tokens are never printed and are passed
to `start` through an exclusive mode-0600 JSON file (or stdin). Evidence and
check inputs receive the same private-file guard, credential-bearing response
fields and URLs are redacted, and no Forgejo credential option exists.
Mutations require a client-owned operation UUID plus the exact rename or
rollback confirmation. State Hub accepts and globally resolves that UUID;
same-intent creation retries remain no-ops after token expiry or a canonical
slug rebind, while changed target/actor intent fails closed. Rollback preflight
and execution are deliberately separate. Wheel force-includes make the
declared `statehub` entry point installable outside the checkout. Verification:
789 repository tests and the 70-page dashboard build passed before the final
retry hardening; the resulting focused suites pass 14 CLI and 7 API tests, and
a clean wheel installation exposes the complete rename command tree. The
dashboard retains the pre-existing `/docs/intakes` broken-link warning.
## Generate a target-repository migration workplan
```task
id: STATE-WP-0085-T06
status: done
priority: high
state_hub_task_id: "154bdbb3-d3bf-5bac-85d7-eecd88545841"
```
Add a generator that inspects a registered repository and produces, without
overwriting an existing file, a repository-native workplan for adopting a new
name. It must use the established workplan prefix from the repository rather
than deriving a new prefix from the proposed slug.
The generated plan must contain ordered tasks and gates for:
1. local/forge cleanliness and immutable baseline capture;
2. repository metadata and workplan-frontmatter preparation;
3. explicit product/runtime naming decisions;
4. consumer, CI, package, deployment, credential-routing, fabric, SBOM, and
documentation inventory;
5. State Hub preflight and approval evidence;
6. Forgejo rename;
7. State Hub identity rebind;
8. fresh-clone registration and `fix-consistency`;
9. identity, work-record, telemetry, route, build, and deployment verification;
10. rollback decision points and commands;
11. soak period, residual handoffs, and final old-checkout cleanup.
The template marks external tasks with their owning repository and never
claims that the target repo can close another repo's work. It starts the live
rename task in `wait` pending human approval.
Acceptance:
- generation is deterministic for the same preflight snapshot;
- the output passes workplan parsing and DoR/quality-debt checks;
- existing State Hub UUID fields are preserved when present and never invented;
- an existing output path is refused unless an explicit safe overwrite mode is
separately designed;
- generated `flex-auth` plan keeps the `FLEX-WP-` prefix and includes every
NetKingdom security-stack risk named in the preflight;
- template snapshot tests cover repositories with active work, archived
workplans, missing local paths, private forge visibility, and old aliases.
Result (2026-08-29): added `statehub repo rename generate-workplan <old>
<new>` with a pure deterministic renderer over the registered repository,
captured rename preflight, indexed workplans, and visible repository metadata.
Prefix discovery uses the repository's declared convention when present and
otherwise requires one unambiguous prefix across active and archived workplan
records; it never derives a prefix from the proposed slug. Number allocation
includes archived records, so the fixed flex-auth snapshot selects
`FLEX-WP-0019` while retaining `FLEX-WP-` task/work-record identity.
The generated ready/proposed plan carries DoR evidence and eleven ordered
tasks for baseline capture, metadata preparation, separate product/runtime
naming decisions, consumer/CI/package/deployment/credential/fabric/SBOM/docs
inventory, renewed preflight and approval, Forgejo rename, State Hub rebind,
fresh-clone registration, relationship-aware verification, rollback, soak,
residuals, and old-checkout cleanup. Live rename and destructive cleanup tasks
start in `wait`. Every captured blocker, warning, active work item, alias, and
external handoff is rendered; an ownership ledger requires exact repository
slugs and prohibits the target plan from closing another repository's work.
Tokens are omitted. Existing UUID-bearing files remain untouched, and no new
`state_hub_workstream_id` or `state_hub_task_id` is invented.
Output uses exclusive create with no overwrite/force mode; private preflight
files retain the mode-0600 input guard. Missing checkouts can render from
indexed records only with an explicit output path and remain visibly blocked
for baseline capture. Snapshot/parser/quality tests cover deterministic
flex-auth output, active work, archived numbering, missing paths, unreadable
private Forge state, old aliases, ambiguous prefixes, UUID preservation, and
overwrite refusal. Verification: 801 Python tests and the 70-page dashboard
build pass; a clean wheel installation exposes the generator command. The
dashboard retains the pre-existing `/docs/intakes` broken-link warning.
## Prove failure recovery and telemetry continuity
```task
id: STATE-WP-0085-T07
status: done
priority: high
state_hub_task_id: "5160ad75-d985-5559-895c-7f8012ea76bc"
```
Build the test and evidence matrix before a live adoption. Include injected
failure after each lifecycle phase, stale preflight, double submission,
conflicting rename, forge redirect, unavailable old slug for rollback,
State Hub outage, edge-outbox replay, unreadable private repository, and a
fresh clone whose remote points at the wrong repository ID.
Compare before/after baselines for repository UUID, workplans, tasks, progress,
decisions, token events and totals, SBOM snapshots, active dispatch, aliases,
messages, and file bindings. Evidence must distinguish a preserved count from
a preserved relationship: equal row counts with records attached to a new UUID
is a failure.
Acceptance:
- automated tests prove safe resume after every injected interruption;
- work-record and telemetry relationship checksums are unchanged;
- old-slug reads remain available through the alias;
- verification fails if a record is detached and recreated even when aggregate
counts match;
- rename events and operational metrics expose phase duration, retries,
failures, rollback, and verification outcome without secrets.
Result (2026-08-29): continuity baselines now retain immutable relationship
tuples and checksums for repository identity, workplans, tasks, progress,
decisions, token events and totals, SBOM, active dispatch, protected routes,
historical messages, and file bindings. Verification compares every original
relationship while permitting append-only history, and phase-aware route checks
prove the old slug remains an alias. Equal-count workplan/token replacements
attached to another repository are rejected. Fresh-clone evidence must attest
the expected Forge numeric ID and baseline head; Forge redirects are rejected
as the wrong coordinate. The operation journal records phase durations,
attempts, retries, failures by phase, rollback attempts/outcome, and verification
outcome. Credential-free phase/failure/verification/rollback/completion events
reference the durable journal without publishing tokens, operator evidence,
URLs, or error details. Automated coverage injects failure after every forward
phase and covers stale evidence, duplicate/conflicting requests, redirects,
rollback route loss and recovery, State Hub outage, edge replay, unreadable
Forge state, and wrong-clone identity. Evidence matrix:
`docs/evidence/STATE-WP-0085-T07-repository-rename-recovery-matrix.md`.
Verification: 810 Python tests and the 70-page dashboard build pass. One known
dashboard `/docs/intakes` to `/suggestions` broken-link warning and one existing
SQLAlchemy async cancellation warning remain outside this task.
## Document operations and repository-boundary handoffs
```task
id: STATE-WP-0085-T08
status: done
priority: medium
state_hub_task_id: "06b6cde4-0428-5467-b2db-d957fde3d8e9"
```
Document the operator workflow, API/CLI contract, dashboard interpretation,
recovery playbook, and target-repository template. Include separate checklists
for repository-only rename and broader product/runtime rename.
Define the handoff format for slug-bearing external sources. A handoff names
the source repository, old/new slug, affected paths or graph IDs, required
reingest/verification, owning workplan/task, and non-secret evidence. It does
not mutate another repository or park the obligation only in prose.
Acceptance:
- an operator can determine the achieved phase and next safe command without
reading source code;
- the runbook says which cleanup is deliberately last and why;
- rollback limitations and irreversible external effects are explicit;
- State Hub/Forgejo/HelixForge/target-repository ownership is unambiguous;
- examples use placeholder credentials and never secret values.
Result (2026-08-29): Delivered the operator runbook in
`docs/repository-rename-operations.md`, the dashboard reference in
`dashboard/src/docs/repository-renames.md`, and the versioned
`state-hub.repository-rename-handoff.v1` schema plus placeholder-only example.
The generated target workplan now requires owner-backed handoff records and
exact fresh-clone/Forge-ID/head evidence before continuity completion. Focused
contract tests pass (29), as do all 813 Python tests and the 71-page dashboard
build. The existing `/docs/intakes` to `/suggestions` broken-link warning and
one existing async cancellation warning remain outside T08.
## Generate and review the flex-auth adoption plan
```task
id: STATE-WP-0085-T09
status: wait
priority: high
state_hub_task_id: "df34f6ec-d137-52d0-93c4-3756a821097f"
```
Blocked on T01-T08 and deployment of the capability.
Run the completed generator for `flex-auth` to `access-engine`, commit the
resulting `FLEX-WP-*` file in the target repository, and synchronize it into
State Hub before any live rename. Review it against the current Forgejo head,
State Hub baselines, active `FLEX-WP-0017`, NetKingdom security-stack
deployments, consumer repositories, fabric records, CI/image publishing, and
credential routes.
This task ends at an approved, repository-owned migration plan. The live
Forgejo rename and security-stack migration execute under that generated plan,
not under `STATE-WP-0085`.
Acceptance:
- the generated workplan is committed and has its own State Hub workplan UUID;
- it records DoR assessment and an explicit human gate for the live rename;
- preflight proves local and Forgejo commits match and captures Forgejo
repository ID `42` plus the existing State Hub repository UUID;
- rollback and "do not remove the old checkout yet" gates are actionable;
- every external consequence has a live work record or a named verification
owner;
- no State Hub, Forgejo, local-clone, deployment, or consumer mutation has been
performed merely to generate the plan.
## Workplan completion
`STATE-WP-0085` can finish only when T01-T08 are complete, T09 has produced the
reviewed repository-owned adoption plan, all implementation tests pass, and the
State Hub capability is deployed where the live migration will call it.
Any remaining product rename, external consumer change, deployment cutover, or
soak obligation must exist as a live record in its owning repository before
this workplan is marked `finished`. The successful `flex-auth` to
`access-engine` live cutover is deliberately not a completion criterion here;
it is the outcome of the generated target-repository workplan.