docs(CUST-WP-0054-T05): Phase 4 sweep checkout migration evidence
Record railiance01 clone tree, host_paths registration, and tooling for bulk clone and path registration. Update T05 workplan with Phase 4 completion.
This commit is contained in:
83
docs/state-hub-railiance01-cutover-phase4-2026-07-06.md
Normal file
83
docs/state-hub-railiance01-cutover-phase4-2026-07-06.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# T05 Phase 4 Evidence — Sweep Checkout Migration (2026-07-06)
|
||||
|
||||
Workplan: `CUST-WP-0054-T05`
|
||||
Parent runbook: `docs/state-hub-railiance01-cutover-plan.md`
|
||||
`no_secret_material_recorded: true`
|
||||
|
||||
## Checklist
|
||||
|
||||
| Step | Action | Result | Evidence |
|
||||
| --- | --- | --- | --- |
|
||||
| 4.1 | Clone root `/home/tegwick/<slug>` | **pass** | 74 host_paths registered; 73 new clones + 4 retried |
|
||||
| 4.2 | Bulk clone from git remotes | **pass** | `railiance01-bulk-clone-repos.sh` on railiance01 |
|
||||
| 4.3 | Register `host_paths` | **pass** | host=`239.62.205.92.host.secureserver.net` × 74 |
|
||||
| 4.4 | Sweep on railiance01 | **pass** | `POST /consistency/sweep/remote-all` exit_code=0 |
|
||||
| 4.5 | Writeback on railiance01 | **pass** | `the-custodian` fix: `push: pushed` from `/home/tegwick/` |
|
||||
|
||||
**Phase 4 status: complete.**
|
||||
|
||||
## Infrastructure changes
|
||||
|
||||
### state-hub image (`main-phase4-sweep`)
|
||||
|
||||
Imported to railiance01 k3s (local build; promote via Forgejo CI next):
|
||||
|
||||
- Dockerfile: `git`, `openssh-client`
|
||||
- `STATE_HUB_SWEEP_HOSTNAME` env (Kubernetes pod hostname cannot contain dots)
|
||||
- `api/classification.py`: railiance01 path fallback for allowed-values YAML
|
||||
- `scripts/consistency_check.py`: `_sweep_hostname()` for `host_paths` resolution
|
||||
|
||||
### Helm (`deploy/railiance/apps/helm/state-hub-values.yaml`)
|
||||
|
||||
```yaml
|
||||
sweep:
|
||||
enabled: true
|
||||
hostname: 239.62.205.92.host.secureserver.net
|
||||
hostPath: /home/tegwick
|
||||
sshHostPath: /home/tegwick/.ssh
|
||||
```
|
||||
|
||||
Deployment mounts host clone tree + SSH config; `postStart` sets
|
||||
`git config --global safe.directory '*'` (root in pod, tegwick-owned repos).
|
||||
|
||||
### railiance01 host
|
||||
|
||||
- SSH: `~/.ssh/config` with `forgejo-remote` + `gitea-remote` (workstation `id_gitea` copied)
|
||||
- Clone tree: `/home/tegwick/<slug>` (77 entries incl. tooling dirs)
|
||||
- Failed-then-fixed slugs: `ihp-railiance-probe`, `llm-connect`, `markitect-project` (from `markitect_project`), `vergabe_teilnahme` (from `vergabe-teilnahme`)
|
||||
|
||||
## Tooling added (the-custodian)
|
||||
|
||||
- `tools/railiance01-bulk-clone-repos.sh`
|
||||
- `tools/railiance01-register-host-paths.sh`
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
# Single-repo fix inside pod — railiance01 path, push succeeds
|
||||
kubectl -n state-hub exec deploy/state-hub -- \
|
||||
python3 /app/scripts/consistency_check.py --repo the-custodian --fix \
|
||||
--api-base http://127.0.0.1:8000
|
||||
# → repo_path: /home/tegwick/the-custodian, push: pushed
|
||||
|
||||
# Full sweep canary
|
||||
curl -s -X POST http://127.0.0.1:8000/consistency/sweep/remote-all \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"max_seconds":300,"source":"phase4-canary"}'
|
||||
# → exit_code: 0, 8 repos processed (budget-limited first pass), skipped_missing: 0
|
||||
```
|
||||
|
||||
`.custodian-brief.md` on railiance01 (`17:08 UTC`) is newer than workstation (`15:27 UTC`).
|
||||
|
||||
Workstation `host_paths[bnt-lap001]` retained for dev; production sweep resolves
|
||||
`host_paths[239.62.205.92.host.secureserver.net]`.
|
||||
|
||||
## Follow-ups (Phase 5)
|
||||
|
||||
- Promote `main-phase4-sweep` image via Forgejo CI (replace hand-imported tag)
|
||||
- Allow longer first-pass sweep budget or multiple cron cycles to reconcile all 74 repos
|
||||
- activity-core scheduled sweeps should recover (were failing with exit_code=1 pre-Phase-4)
|
||||
|
||||
## Next: Phase 5
|
||||
|
||||
48–72h stabilization before coulombcore hub teardown.
|
||||
73
tools/railiance01-bulk-clone-repos.sh
Executable file
73
tools/railiance01-bulk-clone-repos.sh
Executable file
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
# Bulk-clone State Hub registered repos onto railiance01 under /home/tegwick/<slug>.
|
||||
# Usage: railiance01-bulk-clone-repos.sh [--dry-run] [--slug SLUG]
|
||||
set -euo pipefail
|
||||
|
||||
API_BASE="${API_BASE:-http://127.0.0.1:8000}"
|
||||
CLONE_ROOT="${CLONE_ROOT:-/home/tegwick}"
|
||||
DRY_RUN=0
|
||||
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
DRY_RUN=1
|
||||
shift
|
||||
fi
|
||||
|
||||
SLUG_FILTER="${1:-}"
|
||||
|
||||
clone_url_for() {
|
||||
local slug="$1" remote="$2"
|
||||
case "${remote}" in
|
||||
forgejo-remote:*|gitea-remote:*) echo "${remote}" ;;
|
||||
ssh://git@92.205.130.254:30022/coulomb/*) echo "gitea-remote:coulomb/${slug}.git" ;;
|
||||
http://92.205.130.254:32166/coulomb/*) echo "gitea-remote:coulomb/${slug}.git" ;;
|
||||
http://gitea.local/worsch/*) echo "gitea-remote:coulomb/${slug}.git" ;;
|
||||
"") echo "gitea-remote:coulomb/${slug}.git" ;;
|
||||
*) echo "gitea-remote:coulomb/${slug}.git" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
repos_json=$(curl -fsS "${API_BASE}/repos/")
|
||||
mapfile -t slugs < <(python3 -c "
|
||||
import json, sys
|
||||
repos = json.load(sys.stdin)
|
||||
filt = sys.argv[1] if len(sys.argv) > 1 else ''
|
||||
for r in sorted(repos, key=lambda x: x['slug']):
|
||||
if filt and r['slug'] != filt:
|
||||
continue
|
||||
print(r['slug'])
|
||||
" <<<"${repos_json}" "${SLUG_FILTER}")
|
||||
|
||||
cloned=0
|
||||
skipped=0
|
||||
failed=0
|
||||
|
||||
for slug in "${slugs[@]}"; do
|
||||
dest="${CLONE_ROOT}/${slug}"
|
||||
if [[ -d "${dest}/.git" ]]; then
|
||||
echo "SKIP ${slug} (exists)"
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
remote=$(python3 -c "
|
||||
import json, sys
|
||||
repos = json.load(sys.stdin)
|
||||
for r in repos:
|
||||
if r['slug'] == sys.argv[1]:
|
||||
print(r.get('remote_url') or '')
|
||||
break
|
||||
" <<<"${repos_json}" "${slug}")
|
||||
url=$(clone_url_for "${slug}" "${remote}")
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
echo "DRY ${slug} <- ${url}"
|
||||
continue
|
||||
fi
|
||||
echo "CLONE ${slug} <- ${url}"
|
||||
if git clone "${url}" "${dest}"; then
|
||||
cloned=$((cloned + 1))
|
||||
else
|
||||
echo "FAIL ${slug}" >&2
|
||||
failed=$((failed + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "done: cloned=${cloned} skipped=${skipped} failed=${failed}"
|
||||
49
tools/railiance01-register-host-paths.sh
Executable file
49
tools/railiance01-register-host-paths.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
# Register railiance01 host_paths for all State Hub repos.
|
||||
# Usage: railiance01-register-host-paths.sh [--dry-run]
|
||||
set -euo pipefail
|
||||
|
||||
API_BASE="${API_BASE:-http://127.0.0.1:8000}"
|
||||
HOST="${RAILIANCE_HOST:-239.62.205.92.host.secureserver.net}"
|
||||
CLONE_ROOT="${CLONE_ROOT:-/home/tegwick}"
|
||||
DRY_RUN=0
|
||||
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
DRY_RUN=1
|
||||
fi
|
||||
|
||||
repos=$(curl -fsS "${API_BASE}/repos/")
|
||||
registered=0
|
||||
skipped=0
|
||||
|
||||
while IFS= read -r slug; do
|
||||
path="${CLONE_ROOT}/${slug}"
|
||||
current=$(python3 -c "
|
||||
import json, sys
|
||||
repos = json.load(sys.stdin)
|
||||
for r in repos:
|
||||
if r['slug'] == sys.argv[1]:
|
||||
print((r.get('host_paths') or {}).get(sys.argv[2], ''))
|
||||
break
|
||||
" <<<"${repos}" "${slug}" "${HOST}")
|
||||
if [[ "${current}" == "${path}" ]]; then
|
||||
echo "OK ${slug}"
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
if [[ "${DRY_RUN}" -eq 1 ]]; then
|
||||
echo "DRY ${slug} host_paths[${HOST}]=${path} (was: ${current:-<empty>})"
|
||||
continue
|
||||
fi
|
||||
curl -fsS -X POST "${API_BASE}/repos/${slug}/paths" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"host\": \"${HOST}\", \"path\": \"${path}\"}" >/dev/null
|
||||
echo "POST ${slug} -> ${path}"
|
||||
registered=$((registered + 1))
|
||||
done < <(python3 -c "
|
||||
import json, sys
|
||||
for r in sorted(json.load(sys.stdin), key=lambda x: x['slug']):
|
||||
print(r['slug'])
|
||||
" <<<"${repos}")
|
||||
|
||||
echo "done: registered=${registered} unchanged=${skipped}"
|
||||
@@ -226,7 +226,11 @@ baseline counts recorded — `docs/state-hub-railiance01-cutover-preflight-2026-
|
||||
**Phase 3 complete** (2026-07-06): access rewired — `state-hub-primary` →
|
||||
railiance01 `10.43.68.154`; activity-core in-cluster URL; `fleet-state-hub-coulombcore`
|
||||
stopped — `docs/state-hub-railiance01-cutover-phase3-2026-07-06.md`. coulombcore
|
||||
writer still frozen (rollback). Phase 4 sweep paths next.
|
||||
writer still frozen (rollback).
|
||||
**Phase 4 complete** (2026-07-06): 74 repos cloned under `/home/tegwick/`,
|
||||
`host_paths[239.62.205.92.host.secureserver.net]` registered, state-hub pod mounts
|
||||
clone tree + sweep hostname override, remote-all sweep exit_code=0 —
|
||||
`docs/state-hub-railiance01-cutover-phase4-2026-07-06.md`. Phase 5 stabilization next.
|
||||
|
||||
## Task: Working-memory and sink path decoupling
|
||||
|
||||
|
||||
Reference in New Issue
Block a user