Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a049a4-ee9f-78e1-9d66-2cb0f9bea3e3
332 lines
16 KiB
Markdown
332 lines
16 KiB
Markdown
# 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`](adr/ADR-001-repository-rename-identity-contract.md). The automated
|
|
recovery proof is
|
|
[`STATE-WP-0085-T07`](evidence/STATE-WP-0085-T07-repository-rename-recovery-matrix.md).
|
|
|
|
## 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
|
|
|
|
1. Generate and review the target-owned adoption plan:
|
|
|
|
```text
|
|
statehub repo rename generate-workplan <old-slug> <new-slug> --json
|
|
```
|
|
|
|
2. Secure all branches, tags, and local changes to Forgejo. Capture the State
|
|
Hub repository UUID, Forge numeric ID, default branch, and head commit.
|
|
3. Inventory every slug-bearing source. Create one live handoff record in each
|
|
owning repository using the format below. Unknown ownership is a blocker.
|
|
4. Resolve or deliberately coordinate active work and drain/replay edge outbox
|
|
writes. A queued receipt is not central evidence.
|
|
5. Choose a client-owned operation UUID and keep it for every retry:
|
|
|
|
```text
|
|
<operation-id> = <uuid-generated-by-operator-or-orchestrator>
|
|
<confirmation> = rename:<state-hub-repository-uuid>:<old-slug>:<new-slug>
|
|
```
|
|
|
|
6. Capture a new preflight immediately before the window. The output file is
|
|
private and single-use; do not commit it:
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
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`:
|
|
|
|
```text
|
|
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:
|
|
|
|
```json
|
|
{
|
|
"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>"
|
|
}
|
|
```
|
|
|
|
```text
|
|
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:
|
|
|
|
```json
|
|
{
|
|
"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
|
|
}
|
|
```
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
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>` after `statehub-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 status` and `verify` as 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:
|
|
|
|
```text
|
|
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:
|
|
|
|
1. a live file-backed workplan/task or residual intake in the owning repository;
|
|
2. a payload conforming to
|
|
[`repository-rename-handoff-v1.schema.json`](schemas/repository-rename-handoff-v1.schema.json),
|
|
attached to or referenced by that work record.
|
|
|
|
The example is
|
|
[`repository-rename-handoff-v1.json`](examples/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.
|
|
- `verified` requires 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 `completed` or intentionally `rolled-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.
|