Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a049a4-ee9f-78e1-9d66-2cb0f9bea3e3
16 KiB
Repository rename operations
This is the operator runbook for a lineage-preserving repository rename within one Forgejo instance and owner. It coordinates State Hub, Forgejo, HelixForge, and repository-owned work without changing the repository UUID or Forge numeric ID. It does not authorize a live rename; the target repository's reviewed migration workplan and a human cutover decision do that.
The identity and failure contract is
ADR-001. The automated
recovery proof is
STATE-WP-0085-T07.
Ownership
| Owner | Owns | Must not do |
|---|---|---|
| State Hub | Repository UUID, canonical slug, protected aliases, verified Forge identity, operation journal, continuity verification, lifecycle events | Rename Forgejo directly, rewrite another repository's source, or register a replacement repository |
| Forgejo | Git repository, numeric repository ID, canonical Forge name, Git data, redirects and Forge-owned settings | Decide State Hub UUID/alias history or consumer completion |
| HelixForge/operator | Cutover window, credentials routed through their approved custodian, Forgejo rename call, pause/continue/rollback decision, destructive cleanup approval | Put credentials in CLI arguments, evidence, Git, State Hub, or chat |
| Target repository | Repository-native migration plan, product/runtime naming decisions, source updates, fresh clone, file-first work records, aggregation of owned handoff evidence | Claim another repository's source change complete |
| External source repository | Its own CI, deployment, policy, fabric, SBOM, credential-route, package, or documentation source and its live handoff work record | Let the target repository edit or close that obligation on its behalf |
Forgejo credentials are never accepted by statehub repo rename. Resolve
credential ownership before the window with warden route find and use the
operator-approved Forgejo/HelixForge mechanism outside State Hub. Examples below
use identifiers and placeholder paths only.
Choose the scope before planning
Repository-only rename checklist
Use this when the Git repository coordinate changes but the product/runtime identity does not.
- Forgejo repository name and canonical clone/web URL change.
- State Hub canonical slug changes on the existing UUID; old slug remains a protected alias.
- Local checkout and automation checkout coordinates are re-registered.
- Repository metadata and intentional live source references are updated.
- Consumers that name the Git coordinate receive owned handoffs.
- Binary, module/import path, API names, environment variables, Kubernetes namespace/services, packages/images, policy vocabulary, and telemetry labels explicitly remain unchanged.
Product/runtime rename checklist
Use this only when separate product decisions authorize broader changes. Treat each item as independently deployable and independently reversible.
- Record keep/rename decisions for binary and module/import path.
- Record compatibility policy for APIs, CLI names, environment variables, configuration keys, policy vocabulary, and user-facing names.
- Plan package/image coordinates, releases, redirects, and deprecation windows without assuming Forge redirects cover registries.
- Plan Kubernetes namespace, Service/DNS, Helm chart/release, secrets paths, dashboards, alerts, and telemetry labels.
- Give each owning repository/platform a live workplan/task handoff.
- Define deployment order, compatibility window, rollback boundary, and irreversible migrations separately from the repository rename.
A repository-only approval never implies the product/runtime checklist.
Prepare the operation
-
Generate and review the target-owned adoption plan:
statehub repo rename generate-workplan <old-slug> <new-slug> --json -
Secure all branches, tags, and local changes to Forgejo. Capture the State Hub repository UUID, Forge numeric ID, default branch, and head commit.
-
Inventory every slug-bearing source. Create one live handoff record in each owning repository using the format below. Unknown ownership is a blocker.
-
Resolve or deliberately coordinate active work and drain/replay edge outbox writes. A queued receipt is not central evidence.
-
Choose a client-owned operation UUID and keep it for every retry:
<operation-id> = <uuid-generated-by-operator-or-orchestrator> <confirmation> = rename:<state-hub-repository-uuid>:<old-slug>:<new-slug> -
Capture a new preflight immediately before the window. The output file is private and single-use; do not commit it:
statehub repo rename preflight <old-slug> <new-slug> \ --operation-id <operation-id> \ --output <private-mode-0600-preflight.json> --json
Proceed only when ok and result.safe_to_apply are true, blockers is empty,
the immutable IDs/head match the approved baseline, and every warning has a
recorded disposition.
Execute one phase at a time
Start the durable journal before asking HelixForge to mutate Forgejo:
statehub repo rename start <old-slug> <new-slug> \
--operation-id <operation-id> \
--preflight-file <private-mode-0600-preflight.json> \
--actor <operator-id> --confirm '<confirmation>' --json
The operation is now preflighted. Apply only the phase named by
next_safe_action:
statehub repo rename apply <operation-id> --phase forge-renamed \
--confirm '<confirmation>' --json
statehub repo rename apply <operation-id> --phase statehub-rebound \
--confirm '<confirmation>' --json
After the State Hub rebind, make a fresh clone from the canonical URL. Do not
trust an old-slug redirect as identity. Verify the Forge numeric ID and head,
register the new path against the existing State Hub UUID, commit canonical
source/frontmatter updates, and run statehub fix-consistency in the new clone.
The private source evidence file must include the assertions enforced by State Hub. Extra non-secret references are allowed:
{
"fresh_clone": true,
"forge_repository_id": 12345,
"head_commit": "0123456789abcdef0123456789abcdef01234567",
"clone_path": "<new-local-checkout>",
"registration_evidence_ref": "<progress-event-or-file-reference>",
"consistency_evidence_ref": "<progress-event-or-file-reference>"
}
statehub repo rename apply <operation-id> --phase source-synced \
--evidence-file <private-mode-0600-source-evidence.json> \
--confirm '<confirmation>' --json
Collect evidence from every owning handoff. The checks file is a JSON object of named booleans; false or missing required checks block the phase:
{
"old-and-new-slugs-one-uuid": true,
"forge-id-and-head": true,
"work-record-relationships": true,
"telemetry-relationships": true,
"external-handoffs-verified": true,
"build-and-deployment-health": true
}
statehub repo rename verify <operation-id> --json
statehub repo rename apply <operation-id> --phase consumers-verified \
--checks-file <private-mode-0600-checks.json> \
--evidence-file <private-mode-0600-consumer-evidence.json> \
--confirm '<confirmation>' --json
statehub repo rename apply <operation-id> --phase completed \
--confirm '<confirmation>' --json
Read status without source code
At any time run:
statehub repo rename status <operation-id> --json
Interpret the stable CLI envelope as follows:
| Field | Meaning |
|---|---|
state |
planned means resumable/incomplete, achieved means completed, failed means the last attempt failed, and rolled-back is terminal rollback. |
phase |
Last durably achieved phase. An error_code does not advance it. |
result.no_op |
The submitted phase was already achieved; operation identity was preserved. |
next_safe_action.action |
Operator action permitted by the recorded phase. |
next_safe_action.command |
Exact credential-free command skeleton for that action. |
result.error_code |
Code from the last failed attempt; inspect it before retrying. |
result.evidence.telemetry |
Phase duration, attempts, retries, failures, rollback, and verification outcome. |
The phase table is the recovery map:
| Achieved phase | What is true | Normal next command |
|---|---|---|
preflighted |
Journal and immutable baseline exist; Forge may still use old name. | Apply forge-renamed. |
forge-renamed |
Forge numeric ID is visible at the new name; State Hub may still be canonical on old slug. | Apply statehub-rebound. |
statehub-rebound |
New slug is canonical and old slug is a protected alias on the same UUID. | Fresh clone/register, then apply source-synced. |
source-synced |
Canonical clone and source projection are recorded. | Verify owned handoffs; apply consumers-verified. |
consumers-verified |
Required checks and handoff evidence passed. | Re-run verify, then apply completed. |
completed |
Rename is terminal; aliases/history remain. | Soak, residual handoff, then separately approved cleanup. |
rollback-preflight |
Rollback safety and irreversible effects are recorded. | Review; use rollback --execute only if still safe. |
rolled-back |
Old canonical coordinate is restored and the attempt remains auditable. | Verify consumers; a new attempt needs a new operation. |
The REST equivalents are repository-UUID addressed:
| CLI operation | REST operation |
|---|---|
| preflight | POST /repos/{repo_id}/rename/preflight |
| start | POST /repos/{repo_id}/rename/operations |
| status | GET /repository-renames/operations/{operation_id} |
| apply phase | POST /repos/{repo_id}/rename/operations/{operation_id}/phases/{phase} |
| verify | GET /repos/{repo_id}/rename/operations/{operation_id}/verify |
| rollback preflight | POST /repos/{repo_id}/rename/operations/{operation_id}/rollback-preflight |
| execute rollback | POST /repos/{repo_id}/rename/operations/{operation_id}/rollback |
Do not construct mutating REST requests when the CLI can express the operation; the CLI supplies confirmation, private-file, redaction, and next-action guards.
Dashboard interpretation
The dashboard is a read surface, not the rename executor.
- Open
/repos/<old-slug>afterstatehub-rebound. The amber alias banner must identify<old-slug>as an alias and link to<new-slug>. - Old and new routes must show the same repository UUID. The canonical page must list the protected alias.
- Review repository dispatch, workplans/tasks, token totals, SBOM, services, capabilities, interface changes, inbox, and stale external-reference notices.
- A stale Fabric/external projection is a visible handoff, not permission for State Hub to rewrite its source.
- The dashboard does not currently render the phase journal. Use
statehub repo rename statusandverifyas the authoritative operational views. - A healthy dashboard count is insufficient: final acceptance requires the
relationship checksums in
verify.
Recovery playbook
Always reuse the same operation UUID. First run status; treat phase as the
achieved state and the error as the failed attempt.
| Observation | Action |
|---|---|
| State Hub unavailable | Stop mutations. Restore central connectivity; then query the operation UUID. Rename phases are not edge-queueable. |
| Preflight expired or evidence/head changed | Do not start. Resolve drift and issue a fresh preflight; keep or abandon the unused client operation UUID deliberately. |
Forge rename committed but journal remains preflighted |
Retry forge-renamed; State Hub recognizes the expected numeric ID at the new coordinate and records a resumed phase. |
| Forge old-slug lookup redirects to new slug | Treat the redirect as compatibility only. Verify numeric ID at the requested canonical coordinate; never use the redirect as identity evidence. |
Journal is forge-renamed |
Prefer statehub-rebound; do not register a new State Hub repository. |
Journal is statehub-rebound but clone evidence is wrong |
Keep both checkouts. Correct the fresh clone/remote/Forge-ID/head evidence and retry source-synced. |
| Consumer check fails | Remain source-synced or consumers-verified; repair the named owner handoff or enter rollback preflight. Do not complete. |
| Same phase submitted twice | A matching retry returns no_op. A changed operation/target/actor is a conflict, not a retry. |
| Edge outbox contains old-slug writes | Pause preflight until replayed or explicitly resolved. Alias-aware central handlers preserve idempotency after rebind. |
| Private Forge repository unreadable | Stop. Restore approved Forge access; never infer identity from a local checkout or redirect. |
Rollback limits
Rollback is a separately confirmed two-step operation:
statehub repo rename rollback <operation-id> \
--confirm 'rollback:<operation-id>' --json
# Only after reviewing safe_to_rollback, blockers, and irreversible effects:
statehub repo rename rollback <operation-id> \
--confirm 'rollback:<operation-id>' --execute --json
Rollback is unavailable when the old Forge slug is claimed, the expected Forge numeric ID/head is not observable, a competing operation exists, or State Hub routes are inconsistent. Never steal/delete a slug to make rollback appear safe.
The following are not automatically reversed:
- consumer commits, deployments, package/image publication, releases, or cache propagation;
- external redirects and links already observed by users/automation;
- credential-route, policy, DNS, namespace, or product/runtime changes;
- fabric/SBOM/source projections owned by another repository;
- the operation journal, lifecycle events, historical messages, aliases, or telemetry evidence.
After source-synced, every reversed external effect needs its own owner
handoff and evidence. If an irreversible effect makes rollback unsafe, continue
forward or stop for an explicit incident decision.
Repository-boundary handoff format
Every external slug-bearing source is represented by both:
- a live file-backed workplan/task or residual intake in the owning repository;
- a payload conforming to
repository-rename-handoff-v1.schema.json, attached to or referenced by that work record.
The example is
repository-rename-handoff-v1.json.
The handoff_id is the live owning task/intake ID—not an informal label. The
record names the source repository, old/new slug, affected paths or graph IDs,
re-ingest procedure, verification checks, owning workplan/task, status, and
non-secret evidence references.
Handoff rules:
- The target repository creates/routes the obligation but does not edit or close another repository's source task.
- The owner acknowledges it in its own file-first workplan and synchronizes it to State Hub.
verifiedrequires evidence from the owner. Equal counts, a message saying "done", or an uncommitted local edit are insufficient.- Evidence contains commit/check/report/receipt references and optional checksums only. Never include credentials, authorization headers, cookies, private preflight tokens, secret-bearing URLs, or secret values.
- Unresolved handoffs become live residual records before the target workplan
finishes. They are never parked only in this runbook,
SCOPE.md, or prose.
Cleanup is deliberately last
The old local checkout is the final removable asset because it is a recovery input, a comparison point, and evidence of the pre-cutover source. Removing it before terminal verification can strand the operator after an external rename, hide unpushed work, and destroy the easiest rollback comparison.
Only remove the old checkout after all of the following:
- operation is
completedor intentionallyrolled-back; - relationship verification passes;
- canonical fresh clone is registered against the same State Hub UUID;
- required owner handoffs are verified or represented as accepted live residuals;
- the soak window covers deployments, automation, policies, packages, alerts, dashboards, and telemetry;
- explicit cleanup approval identifies the exact old path and recovery source.
Record what was removed and whether recovery remains possible from Forgejo. The protected old slug alias is not cleanup material in this workflow; alias retirement requires a separate compatibility decision and operation.