fix(argocd): pin activity-core child to live 8884b12 and add pin drift check
Two attended activity-core releases patched the live Application without committing the pin, so a hand sync of railiance-apps-root would have rolled activity-core back to ce9a8f8. Git now matches live (no sync run). make argocd-pin-drift compares all railiance01 child pins with live and is required before root syncs. informed-decision drift is flagged for its owner. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Assistant: claude-code Assistant-Model: opus Assistant-Process: 244572@bnt-lap001 Assistant-Session: 3b235e1a-ae45-4d0f-ae70-17a9bd78ece8
This commit is contained in:
parent
53d34bc4f4
commit
141609f96f
6 changed files with 137 additions and 1 deletions
3
Makefile
3
Makefile
|
|
@ -451,6 +451,9 @@ argocd-repo-apply: ## Apply a SOPS-encrypted ArgoCD repository Secret (set ARGOC
|
|||
(echo "ERROR: set ARGOCD_REPOSITORY_SECRET=argocd/repositories/<repo>.repository.sops.yaml"; exit 1)
|
||||
sops -d $(ARGOCD_REPOSITORY_SECRET) | $(KUBECTL) apply -f -
|
||||
|
||||
argocd-pin-drift: ## Read-only: compare railiance01 child Application pins in Git with live (run before any root sync)
|
||||
python3 scripts/argocd_child_pin_drift.py
|
||||
|
||||
argocd-status: ## Show Railiance ArgoCD projects, root app, and registered repos
|
||||
$(KUBECTL) get appprojects.argoproj.io -n $(ARGOCD_NAMESPACE) \
|
||||
railiance-bootstrap railiance-tenants railiance-platform-addons
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
| --- | --- | --- | --- | --- |
|
||||
| workplan | RPF-WP-ADHOC-2026-10-10 | finished | — | workplans/ADHOC-2026-10-10.md |
|
||||
| workplan | RPF-WP-ADHOC-2026-10-11 | finished | — | workplans/ADHOC-2026-10-11.md |
|
||||
| workplan | RPF-WP-ADHOC-2026-10-19 | active | — | workplans/ADHOC-2026-10-19.md |
|
||||
| workplan | RPF-WP-0015 | active | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
||||
| workplan | RPF-WP-0025 | finished | — | workplans/RPF-WP-0025-openbao-operator-only-access.md |
|
||||
| workplan | RPF-WP-0027 | blocked | — | workplans/RPF-WP-0027-keycape-live-secret-exposure-recovery.md |
|
||||
|
|
@ -44,6 +45,9 @@
|
|||
| task | RPF-WP-ADHOC-2026-10-11-T01 | done | — | workplans/ADHOC-2026-10-11.md |
|
||||
| task | RPF-WP-ADHOC-2026-10-11-T02 | done | — | workplans/ADHOC-2026-10-11.md |
|
||||
| task | RPF-WP-ADHOC-2026-10-11-T03 | done | — | workplans/ADHOC-2026-10-11.md |
|
||||
| task | RPF-WP-ADHOC-2026-10-19-T01 | done | — | workplans/ADHOC-2026-10-19.md |
|
||||
| task | RPF-WP-ADHOC-2026-10-19-T02 | done | — | workplans/ADHOC-2026-10-19.md |
|
||||
| task | RPF-WP-ADHOC-2026-10-19-T03 | wait | — | workplans/ADHOC-2026-10-19.md |
|
||||
| task | RPF-WP-0015-T01 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
||||
| task | RPF-WP-0015-T02 | done | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
||||
| task | RPF-WP-0015-T03 | wait | — | workplans/RPF-WP-0015-audit-core-custody-and-recovery-coordination.md |
|
||||
|
|
|
|||
|
|
@ -12,3 +12,8 @@ with the founder's go-ahead: no `automated` block, no finalizer, and a pinned
|
|||
`targetRevision`. A merge here plus a hand sync of the root is a live
|
||||
production change on railiance01. Drafts wait in `../drafts/`, which no root
|
||||
syncs.
|
||||
|
||||
A release that changes a child's `targetRevision` commits that pin here in the
|
||||
same change window, whether or not the live Application was patched directly.
|
||||
Before any hand sync of the root, run `make argocd-pin-drift`; any `DRIFT`
|
||||
line means the sync would roll that child back, so stop and reconcile first.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
# ACTIVITY-WP-0041 / RPF-WP-0048: founder-authorized adoption, 2026-09-27.
|
||||
# Pin = live revision of the 2026-10-19 founder-approved release
|
||||
# (activity-core docs/evidence/2026-10-19-grant-v2-lease-expiry-release.json).
|
||||
# Every activity-core release commits its pin here; see RPF-WP-ADHOC-2026-10-19.
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
|
|
@ -10,7 +13,7 @@ spec:
|
|||
project: activity-core
|
||||
source:
|
||||
repoURL: https://forgejo.coulomb.social/coulomb/activity-core.git
|
||||
targetRevision: ce9a8f8edf4ec162bae3aa087803bd57a1b33a66
|
||||
targetRevision: 8884b123c54877bec44fb8355792244a8353a8ff
|
||||
path: k8s/gitops
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
|
|
|
|||
61
scripts/argocd_child_pin_drift.py
Normal file
61
scripts/argocd_child_pin_drift.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Read-only drift check between child Application pins in Git and on railiance01.
|
||||
|
||||
`railiance-apps-root` syncs by hand from `argocd/railiance01/applications`. If a
|
||||
release patches a child's live targetRevision without committing the same pin
|
||||
here, the next root sync silently rolls that child back. Run this before any
|
||||
root sync and after every release. Exit 1 on drift, 2 on a read failure.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import json
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
import yaml
|
||||
|
||||
APPS_DIR = Path(__file__).resolve().parent.parent / "argocd/railiance01/applications"
|
||||
|
||||
|
||||
def declared() -> dict[str, str]:
|
||||
pins = {}
|
||||
for path in sorted(APPS_DIR.glob("*.y*ml")):
|
||||
for doc in yaml.safe_load_all(path.read_text()):
|
||||
if doc and doc.get("kind") == "Application":
|
||||
pins[doc["metadata"]["name"]] = doc["spec"]["source"]["targetRevision"]
|
||||
return pins
|
||||
|
||||
|
||||
def live() -> dict[str, str]:
|
||||
out = subprocess.run(
|
||||
["kubectl", "-n", "argocd", "get", "applications.argoproj.io", "-o", "json"],
|
||||
capture_output=True, text=True, check=True,
|
||||
).stdout
|
||||
return {
|
||||
item["metadata"]["name"]: item["spec"].get("source", {}).get("targetRevision", "")
|
||||
for item in json.loads(out)["items"]
|
||||
}
|
||||
|
||||
|
||||
def main() -> int:
|
||||
try:
|
||||
running = live()
|
||||
except (subprocess.CalledProcessError, FileNotFoundError) as exc:
|
||||
print(f"ERROR: cannot read live Applications: {exc}", file=sys.stderr)
|
||||
return 2
|
||||
drift = 0
|
||||
for name, pin in declared().items():
|
||||
current = running.get(name)
|
||||
if current == pin:
|
||||
print(f"ok {name} {pin[:12]}")
|
||||
else:
|
||||
drift += 1
|
||||
print(f"DRIFT {name} git={pin[:12]} live={(current or 'absent')[:12]}")
|
||||
if drift:
|
||||
print(f"{drift} child pin(s) differ: commit the live pin here (or revert live) "
|
||||
"before syncing railiance-apps-root.", file=sys.stderr)
|
||||
return 1 if drift else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
60
workplans/ADHOC-2026-10-19.md
Normal file
60
workplans/ADHOC-2026-10-19.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
---
|
||||
id: RPF-WP-ADHOC-2026-10-19
|
||||
type: workplan
|
||||
title: "Reconcile railiance01 child Application pins with live and guard root syncs"
|
||||
domain: financials
|
||||
repo: railiance-platform
|
||||
status: active
|
||||
flavor: implementation
|
||||
owner: claude-code
|
||||
topic_slug: railiance
|
||||
created: "2026-10-19"
|
||||
updated: "2026-10-19"
|
||||
---
|
||||
|
||||
## Align the activity-core pin with the founder-approved live release
|
||||
|
||||
```task
|
||||
id: RPF-WP-ADHOC-2026-10-19-T01
|
||||
status: done
|
||||
priority: high
|
||||
```
|
||||
|
||||
The activity-core releases on 2026-10-04 (`a86a143`) and 2026-10-19 (`8884b12`)
|
||||
patched the live child Application without committing its pin here. Git still
|
||||
pinned `ce9a8f8` (2026-09-28), and `railiance-apps-root` was OutOfSync, so the
|
||||
next hand root sync would have rolled activity-core back by two releases. The
|
||||
pin is now `8884b123c54877bec44fb8355792244a8353a8ff`, which equals live. This
|
||||
is a Git-only change: live state is already at this revision, and no sync was
|
||||
run.
|
||||
|
||||
## Add a read-only pin drift check before root syncs
|
||||
|
||||
```task
|
||||
id: RPF-WP-ADHOC-2026-10-19-T02
|
||||
status: done
|
||||
priority: medium
|
||||
```
|
||||
|
||||
`make argocd-pin-drift` (`scripts/argocd_child_pin_drift.py`) compares every
|
||||
child Application's `targetRevision` in `argocd/railiance01/applications` with
|
||||
live, and exits 1 on drift. The applications README now requires it before any
|
||||
hand sync of the root. activity-core `docs/gitops-release.md` now requires
|
||||
releases to commit their pin here and pass this check.
|
||||
|
||||
## informed-decision pin drift (owner confirmation)
|
||||
|
||||
```task
|
||||
id: RPF-WP-ADHOC-2026-10-19-T03
|
||||
status: wait
|
||||
needs_human: true
|
||||
priority: medium
|
||||
```
|
||||
|
||||
Live informed-decision runs `312ab97e876e…` ("Prepare owner-reviewed zlib-only
|
||||
native release decision", 2026-10-11). Git pins `9207a513` (2026-10-05). Live is
|
||||
a descendant of the pin, so this is likely the same skipped-pin pattern. It is
|
||||
not changed here, because this session cannot verify that `312ab97` was an
|
||||
admitted release. Once the informed-decision owner confirms it, commit
|
||||
that pin. Until then, do not hand sync `railiance-apps-root`, because the sync
|
||||
would revert informed-decision.
|
||||
Loading…
Add table
Add a link
Reference in a new issue