Complete ISSUE-WP-0007 railiance01 cutover
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

This commit is contained in:
tegwick 2026-08-19 23:53:41 +02:00
parent 3523a7decc
commit 51440da148
8 changed files with 35 additions and 344 deletions

View file

@ -112,8 +112,8 @@ also `sync_metadata.mapping` and a row in `mappings.db`.
| Artifact | Provides |
| --- | --- |
| PyPI package | Built wheel/sdist; Makefile targets publish to Coulomb Gitea/Forgejo registry |
| Docker + `k8s/railiance/` | GitOps-style deploy on railiance01 (deployment, service, configmap, ExternalSecret) |
| `docs/argocd-gitops.md` | Operator runbook for that deploy |
| Docker image | Built and published by this app repo; production consumes an immutable digest |
| `rapp-issue-core` | Authoritative railiance01 runtime package: manifests, private Service, ExternalSecret references, NetworkPolicy, rollout, rollback, and live evidence |
| `docs/package-release.md` | Release notes for packaging |
| `registry/` + `CAPABILITY-issue-tracking.yaml` | Reuse-surface / capability federation metadata |
| Tests | `tests/` unit coverage for models, CLI, backends, API ingest/query, mapping |
@ -313,11 +313,14 @@ issue_core/
cli/ # Click: issue, project, map, backend, sync, serve
api/ # FastAPI: ingest + query (+ auth, schemas)
tests/
docs/ # gitops, nats stub, mapping design, boundary-sync policy
k8s/railiance/ # deploy manifests
docs/ # nats stub, mapping design, boundary-sync policy
workplans/ # ADR-001 work items for this repo
```
Production runtime packaging is intentionally outside this tree in the sibling
`rapp-issue-core` repository. This repo remains authoritative for domain code,
tests, image construction, and Forgejo image publication.
| Concern | Choice |
| --- | --- |
| Language | Python 3.8+ |
@ -339,7 +342,7 @@ workplans/ # ADR-001 work items for this repo
- `docs/boundary-sync-and-status-mapping.md` — dual-lifecycle outward policy (v1)
- `docs/intent-work-record-alignment-review.md` — INTENT vs work-record audit
- `docs/nats-task-ingestion.md` — NATS design stub
- `docs/argocd-gitops.md` — railiance01 deploy
- `rapp-issue-core` — authoritative railiance01 deployment and live evidence
- `AGENT_INTEGRATION.md` — library patterns for tracker ops
- `workplans/ISSUE-WP-0004-align-with-work-record-canon.md` — initial framing pivot
- `workplans/ISSUE-WP-0005-connector-alignment-implementation.md`**finished**: mapping + scope gaps

View file

@ -1,168 +0,0 @@
# ArgoCD GitOps deployment - railiance01
This runbook captures the issue-core side of the railiance01 GitOps pilot.
It keeps secrets out of Git and leaves platform-owned bootstrap steps in
railiance-platform.
## Source layout
- Workload bundle: `issue-core/k8s/railiance/`
- Image: `forgejo.coulomb.social/coulomb/issue-core:0.2.1`
- Container port and Service port: `8765`
- Cluster Service URL: `http://issue-core.issue-core.svc.cluster.local:8765`
- Tenant Application: `railiance-platform/argocd/applications/issue-core.application.yaml`
The `Application` should point at this repo's `k8s/railiance` path and use
`CreateNamespace=true` for the `issue-core` namespace. The namespace itself is
therefore intentionally not duplicated in this bundle.
## Platform gates
The following pieces are owned by railiance-platform for the live pilot and for
any future cluster replay:
- ArgoCD repository credentials and the project/app-of-apps convention.
- The `issue-core` ArgoCD `Application`.
- External Secrets Operator and a `ClusterSecretStore` named `openbao`.
- OpenBao entries for the issue-core runtime Secret.
For the 2026-06-25 live deployment, these gates were satisfied and the
`issue-core` Application reached Synced/Healthy with image `0.2.1`.
## Secret contract
Kubernetes Secret name: `issue-core-runtime`
Current issue-core manifest path:
```text
platform/workloads/issue-core/issue-core/issue-core-runtime
```
Credential custody is owned by railiance-platform/OpenBao. For agents, first
use the non-secret route catalog entry `activity-core-issue-sink` to confirm
the activity-core + issue-core pairing, and never request the value from
ops-warden.
Required properties:
- `ISSUE_CORE_API_KEY` - shared ingestion key used by issue-core and
activity-core.
- `GITEA_BACKEND_TOKEN` - token for creating issues in cluster Gitea.
Never write either value to Git, State Hub, workplans, logs, or chat. Record
only non-secret evidence such as Secret key count, ExternalSecret readiness,
HTTP status codes, and created issue URLs.
## Build and publish
Build the checked-out source tree and publish a registry tag that ArgoCD can
pull:
```bash
docker build -t forgejo.coulomb.social/coulomb/issue-core:0.2.1 .
docker push forgejo.coulomb.social/coulomb/issue-core:0.2.1
```
The Coulomb Gitea package is public-pullable for this image, so the workload
does not use an `imagePullSecret`.
## Pre-sync validation
From the issue-core repo:
```bash
kubectl kustomize k8s/railiance
```
The rendered resources should be:
- `ExternalSecret/issue-core-runtime`
- `ConfigMap/issue-core-backends`
- `Deployment/issue-core`
- `Service/issue-core`
## Sync verification
After railiance-platform syncs the tenant `Application`:
```bash
kubectl get application issue-core -n argocd
kubectl -n issue-core get externalsecret issue-core-runtime
kubectl -n issue-core get secret issue-core-runtime
kubectl -n issue-core get deploy,pod,svc
```
Expected non-secret evidence:
- ArgoCD Application reports `Synced` and `Healthy`.
- `ExternalSecret/issue-core-runtime` reports Ready.
- `Secret/issue-core-runtime` exists with two data keys.
- `Deployment/issue-core` has one available replica.
- `Service/issue-core` exposes port `8765`.
Health check from inside the cluster:
```bash
kubectl -n issue-core run issue-core-health --rm -i --restart=Never --image=curlimages/curl:8.8.0 -- http://issue-core:8765/healthz
```
## Ingestion smoke
Run the authenticated smoke from a short-lived Job so the API key is mounted
from the Kubernetes Secret without printing it:
```bash
kubectl -n issue-core delete job issue-core-smoke --ignore-not-found
kubectl -n issue-core apply -f - <<'YAML'
apiVersion: batch/v1
kind: Job
metadata:
name: issue-core-smoke
spec:
ttlSecondsAfterFinished: 600
backoffLimit: 0
template:
spec:
restartPolicy: Never
containers:
- name: smoke
image: curlimages/curl:8.8.0
env:
- name: ISSUE_CORE_API_KEY
valueFrom:
secretKeyRef:
name: issue-core-runtime
key: ISSUE_CORE_API_KEY
command: ["/bin/sh", "-ceu"]
args:
- |
curl -fsS -X POST "http://issue-core:8765/issues/" -H "Authorization: Bearer ${ISSUE_CORE_API_KEY}" -H "Content-Type: application/json" --data '{"title":"issue-core railiance01 smoke","description":"GitOps smoke created by the issue-core deployment runbook.","target_repo":"coulomb/markitect-main","priority":"low","labels":["smoke","issue-core"],"source_type":"rule","source_id":"issue-core-gitops-smoke","triggering_event_id":"scheduled","activity_definition_id":"issue-core-gitops-smoke"}'
YAML
kubectl -n issue-core wait --for=condition=complete job/issue-core-smoke --timeout=90s
kubectl -n issue-core logs job/issue-core-smoke
```
Acceptance evidence is HTTP 201 plus a response body containing `issue_id`,
`backend: "gitea"`, and an `issue_url` for cluster Gitea.
Cleanup:
```bash
kubectl -n issue-core delete job issue-core-smoke
```
## Activity-core handoff
After issue-core is Ready and the shared `ISSUE_CORE_API_KEY` is available to
activity-core from the same approved OpenBao source:
- Set `ISSUE_CORE_URL=http://issue-core.issue-core.svc.cluster.local:8765`.
- Set `ISSUE_SINK_TYPE=rest`.
- Inject the same `ISSUE_CORE_API_KEY` into the activity-core worker.
- Keep cron-triggered emissions explicit: `triggering_event_id` may be a stable
non-empty scheduler key such as `scheduled`; event-driven emissions should
continue to send the event UUID.
Verify by running an activity-core emission and confirming that issue-core
returns HTTP 201 and creates a Gitea issue.

View file

@ -1,25 +0,0 @@
# Non-secret backend structure for issue-core on CoulombCore.
# Default backend = Forgejo task inbox (Gitea-compatible API). Legacy in-cluster
# gitea-http was retired; all coulomb/* repos there are archived (issue create 423).
# Token is injected at startup from GITEA_BACKEND_TOKEN (ExternalSecret) — must be
# a Forgejo API token with issues:create on the default repo.
apiVersion: v1
kind: ConfigMap
metadata:
name: issue-core-backends
namespace: issue-core
labels:
app.kubernetes.io/name: issue-core
app.kubernetes.io/part-of: railiance-gitops
data:
backends.json: |
{
"forgejo-inbox": {
"type": "gitea",
"base_url": "https://forgejo.coulomb.social",
"owner": "coulomb",
"repo": "the-custodian",
"token": "__FROM_ENV__"
},
"default": "forgejo-inbox"
}

View file

@ -1,72 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: issue-core
namespace: issue-core
labels:
app.kubernetes.io/name: issue-core
app.kubernetes.io/part-of: railiance-gitops
annotations:
argocd.argoproj.io/sync-wave: "1" # after the ExternalSecret (wave 0)
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: issue-core
template:
metadata:
labels:
app.kubernetes.io/name: issue-core
app.kubernetes.io/part-of: railiance-gitops
spec:
# Image is public-pullable from the Gitea registry (per railiance-forge
# docs). Add imagePullSecrets: [{name: gitea-registry}] if it becomes private.
containers:
- name: issue-core
image: forgejo.coulomb.social/coulomb/issue-core:0.2.1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8765
env:
- name: ISSUE_CORE_API_KEY
valueFrom:
secretKeyRef:
name: issue-core-runtime
key: ISSUE_CORE_API_KEY
- name: GITEA_BACKEND_TOKEN
valueFrom:
secretKeyRef:
name: issue-core-runtime
key: GITEA_BACKEND_TOKEN
- name: BACKENDS_TEMPLATE
valueFrom:
configMapKeyRef:
name: issue-core-backends
key: backends.json
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 20
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
runAsNonRoot: true
runAsUser: 10001
capabilities:
drop: ["ALL"]

View file

@ -1,37 +0,0 @@
# Runtime secrets for issue-core, materialized from OpenBao by External Secrets
# Operator (cluster default per railiance-platform docs/argocd-gitops.md).
#
# DEPENDENCY: External Secrets Operator is not yet installed on railiance01 and
# the OpenBao path below must be provisioned by railiance-platform. Until then
# this resource will not reconcile and the Deployment stays Pending the Secret.
#
# OpenBao path: platform/workloads/issue-core/issue-core/issue-core-runtime
# properties: ISSUE_CORE_API_KEY, GITEA_BACKEND_TOKEN
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: issue-core-runtime
namespace: issue-core
labels:
app.kubernetes.io/name: issue-core
app.kubernetes.io/part-of: railiance-gitops
annotations:
argocd.argoproj.io/sync-wave: "0" # before the Deployment (wave 1)
spec:
refreshInterval: 1h
secretStoreRef:
# Provisioned by railiance-platform during ESO install; name TBC on bootstrap.
name: openbao
kind: ClusterSecretStore
target:
name: issue-core-runtime
creationPolicy: Owner
data:
- secretKey: ISSUE_CORE_API_KEY
remoteRef:
key: platform/workloads/issue-core/issue-core/issue-core-runtime
property: ISSUE_CORE_API_KEY
- secretKey: GITEA_BACKEND_TOKEN
remoteRef:
key: platform/workloads/issue-core/issue-core/issue-core-runtime
property: GITEA_BACKEND_TOKEN

View file

@ -1,12 +0,0 @@
# issue-core workload manifests, synced by the ArgoCD `issue-core` Application
# (path k8s/railiance, destination namespace issue-core, CreateNamespace=true).
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: issue-core
resources:
- externalsecret.yaml
- configmap-backends.yaml
- deployment.yaml
- service.yaml

View file

@ -1,19 +0,0 @@
# ClusterIP exposing issue-core on 8765 as
# issue-core.issue-core.svc.cluster.local:8765 — the address activity-core's
# ISSUE_CORE_URL points at once its k8s runtime port is corrected (8010 -> 8765).
apiVersion: v1
kind: Service
metadata:
name: issue-core
namespace: issue-core
labels:
app.kubernetes.io/name: issue-core
app.kubernetes.io/part-of: railiance-gitops
spec:
type: ClusterIP
selector:
app.kubernetes.io/name: issue-core
ports:
- name: http
port: 8765
targetPort: http

View file

@ -4,7 +4,7 @@ type: workplan
title: "rapp-issue-core: move the runtime off CoulombCore onto railiance01"
domain: infotech
repo: issue-core
status: active
status: finished
owner: codex
lane: yellow
topic_slug: issue-core
@ -176,7 +176,7 @@ UV_CACHE_DIR=/tmp/issue-core-rmgr-cache uv run --directory ../repo-manager \
```task
id: ISSUE-WP-0007-T02
status: progress
status: done
priority: high
state_hub_task_id: "e0427de0-dfac-4848-ba2d-1421167eb704"
```
@ -193,7 +193,7 @@ records by hand.
```task
id: ISSUE-WP-0007-T03
status: todo
status: done
priority: high
state_hub_task_id: "d6e5e2ac-7f42-412f-8143-91531f5155e8"
```
@ -214,7 +214,7 @@ handoff. Two active sources of deployment truth are not allowed.
```task
id: ISSUE-WP-0007-T04
status: todo
status: done
priority: high
state_hub_task_id: "21974fbd-4962-4732-b407-c531bb2ebfe6"
```
@ -240,7 +240,7 @@ That is the condition being retired.
```task
id: ISSUE-WP-0007-T05
status: todo
status: done
priority: high
state_hub_task_id: "3fdbd9ab-3e3f-4ac0-8763-77278a1cc6e6"
```
@ -263,7 +263,7 @@ still pointed at `127.0.0.1:18765`.
```task
id: ISSUE-WP-0007-T06
status: todo
status: done
priority: medium
state_hub_task_id: "668e37dc-8ebe-46cc-9a27-905440b1240c"
```
@ -291,3 +291,24 @@ Remaining CoulombCore services after this: `core-hub-staging` and `inter-hub`.
kubeconfig any more — its `k3s-api` tunnel was removed on 2026-08-19, so
inspecting the *current* deployment needs that tunnel restored first.
- Reviewed 2026-08-19 in issue-core and promoted from `proposed` to `ready`.
## Completion evidence — 2026-08-19
- Created, registered, validated, and published private repo
`coulomb/rapp-issue-core`; wrapper commit `3003c92`, State Hub workstream
`b4c39759-6073-4558-a70c-2f869a31cfca`.
- Fixed the Forgejo lifecycle identifier contract in issue-core commit
`3523a7d`; published and deployed immutable digest
`sha256:a56c80ccbd1690bc0bcea384165b1967704f0fdbaf1c506ce1082f56190b92cf`.
- Full wrapper evidence passed: health 200, unauthenticated 401, authenticated
create 201 + close 200, bounded dependency 503, recovery 201 + close 200,
ExternalSecret ready with two keys, both NetworkPolicies present, previous
digest rollback health 200, and restored-digest re-verification.
- activity-core commit `213e810` now uses
`http://issue-core.issue-core.svc.cluster.local:8765` directly while keeping
`ISSUE_SINK_TYPE=state-hub`; a controlled worker-side request returned
create 201 and close 200.
- Deleted the legacy activity-core bridge resources, stopped and removed both
port-18765 ops-bridge entries, and confirmed the local listener was gone.
- Retired this repo's duplicate `k8s/railiance/` source and obsolete ArgoCD
runbook after the wrapper was pushed and its evidence retained.