diff --git a/.custodian-brief.md b/.custodian-brief.md index 112db39..53b4df1 100644 --- a/.custodian-brief.md +++ b/.custodian-brief.md @@ -2,12 +2,21 @@ # Custodian Brief — issue-core **Domain:** infotech -**Last synced:** 2026-07-02 12:50 UTC +**Last synced:** 2026-06-23 12:26 UTC **State Hub:** http://127.0.0.1:8000 *(adjust if running on a remote machine)* ## Active Workstreams -*(none — repo may need first-session setup)* +### Deploy issue-core as a service on railiance01 (ArgoCD GitOps pilot) +Progress: 1/7 done | workstream_id: `896ace77-21b3-450b-8fb7-254aefc8c570` + +**Open tasks:** +- ! ArgoCD bootstrap (railiance-platform dependency) + issue-core Application `9b199b1d` +- ! OpenBao secret: ISSUE_CORE_API_KEY `ad52527f` +- ► Kubernetes manifests (namespace, Deployment, Service) in GitOps source `38887dd6` +- ► In-cluster backend config (cluster Gitea / markitect) `10923f1e` +- ► Wire activity-core to the live service `96b14cdb` +- ► End-to-end verification + GitOps runbook `8d853b8e` --- ## MCP Orientation (when available) diff --git a/.forgejo/workflows/ci-smoke.yaml b/.forgejo/workflows/ci-smoke.yaml deleted file mode 100644 index bd44c56..0000000 --- a/.forgejo/workflows/ci-smoke.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# Canonical CI smoke template (tier 1 routing drill). -# Copy to: .forgejo/workflows/ci-smoke.yaml in consumer repos. -name: CI Smoke - -on: - push: - branches: - - main - workflow_dispatch: - -jobs: - host-smoke: - runs-on: self-hosted - steps: - - name: Routing probe (host runner) - run: | - set -eu - echo "repository=${GITHUB_REPOSITORY:-unknown}" - echo "sha=${GITHUB_SHA:-unknown}" - echo "runner=${RUNNER_NAME:-unknown}" - uname -a - - container-smoke: - runs-on: ubuntu-latest - steps: - - name: Routing probe (container label) - run: | - set -eu - echo "container-smoke ok for ${GITHUB_REPOSITORY:-unknown}" \ No newline at end of file diff --git a/.forgejo/workflows/image.yaml b/.forgejo/workflows/image.yaml deleted file mode 100644 index de0be52..0000000 --- a/.forgejo/workflows/image.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: Build and Publish Container Image - -on: - push: - branches: - - main - paths: - - ".forgejo/workflows/image.yaml" - - "Dockerfile" - - "issue_core/**" - - "docker-entrypoint.sh" - workflow_dispatch: - -env: - REGISTRY: forgejo.coulomb.social - IMAGE_NAME: coulomb/issue-core - DOCKER_HOST: tcp://127.0.0.1:2375 - -jobs: - build-and-push: - runs-on: container-build - steps: - - name: Build and push image - env: - REGISTRY_USER: ${{ secrets.REGISTRY_USER }} - REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} - run: | - set -eu - REF="${GITHUB_SHA:-main}" - SHORT="${REF:0:7}" - mkdir -p buildctx "${HOME}/bin" - wget -qO /tmp/repo.tar.gz \ - "https://forgejo.coulomb.social/${GITHUB_REPOSITORY}/archive/${SHORT}.tar.gz" - tar xzf /tmp/repo.tar.gz -C buildctx --strip-components=1 - wget -qO- https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz \ - | tar xz --strip-components=1 -C "${HOME}/bin" docker/docker - export PATH="${HOME}/bin:${PATH}" - echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" -u "${REGISTRY_USER}" --password-stdin - IMAGE="${REGISTRY}/${IMAGE_NAME}" - docker build -t "${IMAGE}:latest" -t "${IMAGE}:main-${SHORT}" buildctx - docker push "${IMAGE}:latest" - docker push "${IMAGE}:main-${SHORT}" - echo "pushed ${IMAGE}:latest and ${IMAGE}:main-${SHORT}" \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index ab74d8c..34b2949 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,10 +1,15 @@ # issue-core REST ingestion service image. # -# Builds the checked-out issue-core[api] package and runs the FastAPI ingestion -# server on :8765. The image is published to -# gitea.coulomb.social/coulomb/issue-core. +# Installs the published issue-core[api] package from the Coulomb Gitea PyPI +# index (no sibling-checkout build context) and runs the FastAPI ingestion +# server on :8765. Built and pushed to gitea.coulomb.social/coulomb/issue-core. FROM python:3.12-slim AS runtime +ARG ISSUE_CORE_VERSION=">=0.2,<0.3" +# Do not name this PIP_INDEX_URL — Docker exposes ARGs as env vars during RUN, +# and pip treats PIP_INDEX_URL as the sole primary index (excluding PyPI). +ARG GITEA_PYPI_INDEX_URL=https://gitea.coulomb.social/api/packages/coulomb/pypi/simple/ + ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ HOME=/home/app @@ -13,11 +18,10 @@ ENV PYTHONUNBUFFERED=1 \ # (~/.config/issue-tracker/backends.json). RUN useradd --create-home --home-dir /home/app --uid 10001 app -WORKDIR /src -COPY pyproject.toml README.md LICENSE ./ -COPY issue_core ./issue_core -RUN pip install --no-cache-dir --index-url https://pypi.org/simple ".[api]" \ - && rm -rf /src +RUN pip install --no-cache-dir \ + --index-url https://pypi.org/simple \ + --extra-index-url "${GITEA_PYPI_INDEX_URL}" \ + "issue-core[api]${ISSUE_CORE_VERSION}" COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh RUN chmod +x /usr/local/bin/docker-entrypoint.sh @@ -26,4 +30,4 @@ USER app EXPOSE 8765 # Entrypoint renders backends.json from env, then execs the server. -ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] \ No newline at end of file +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] diff --git a/docs/argocd-gitops.md b/docs/argocd-gitops.md index fa8da2a..3df5d0b 100644 --- a/docs/argocd-gitops.md +++ b/docs/argocd-gitops.md @@ -7,7 +7,7 @@ railiance-platform. ## Source layout - Workload bundle: `issue-core/k8s/railiance/` -- Image: `gitea.coulomb.social/coulomb/issue-core:0.2.1` +- Image: `gitea.coulomb.social/coulomb/issue-core:0.2.0` - 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` @@ -18,31 +18,31 @@ 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: +The following pieces are owned by railiance-platform before the workload can +be fully reconciled: - 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`. +Until those gates exist, `kubectl kustomize k8s/railiance` can render locally, +but the live `ExternalSecret` and `Deployment` are expected to wait. ## Secret contract Kubernetes Secret name: `issue-core-runtime` -Current issue-core manifest path: +Current issue-core manifest path, pending railiance-platform confirmation: ```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. +Credential route catalog id `issue-core-ingestion-api-key` is owned by +railiance-platform/OpenBao and is still marked draft/path TBD in the local +ops-warden catalog reviewed 2026-06-18. Confirm the canonical path before +provisioning the live Secret. Required properties: @@ -56,12 +56,12 @@ 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: +Use the published package as the image input. For a reproducible release image, +pin the package version to the image tag: ```bash -docker build -t gitea.coulomb.social/coulomb/issue-core:0.2.1 . -docker push gitea.coulomb.social/coulomb/issue-core:0.2.1 +docker build --build-arg ISSUE_CORE_VERSION="==0.2.0" -t gitea.coulomb.social/coulomb/issue-core:0.2.0 . +docker push gitea.coulomb.social/coulomb/issue-core:0.2.0 ``` The Coulomb Gitea package is public-pullable for this image, so the workload @@ -137,7 +137,7 @@ spec: 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"}' + 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_project","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 diff --git a/integration/gitea-backend.integration.yaml b/integration/gitea-backend.integration.yaml deleted file mode 100644 index b26b6cb..0000000 --- a/integration/gitea-backend.integration.yaml +++ /dev/null @@ -1,82 +0,0 @@ -schema_version: open-reuse.integration.v0.1 -id: issue-core-gitea -name: issue-core Gitea Backend -description: > - Pluggable remote backend that maps the issue-core unified task model onto the - Gitea issues API for cross-repo task landing and synchronization. -status: registered -owner: issue-core - -local: - repo: issue-core - path: integration/gitea-backend.integration.yaml - system: issue-core - -upstream: - name: Gitea - project_url: https://github.com/go-gitea/gitea - homepage: https://about.gitea.com/ - version_policy: gitea-api-v1 - monitor: - releases: true - tags: true - security_advisories: true - license_changes: true - -reuse: - primary_reuse_mode: adapter - secondary_reuse_modes: - - plugin - risk_level: medium - rationale: > - Gitea REST API is wrapped behind the RemoteBackend interface; local task - lifecycle semantics remain stable across backend swaps. - -boundary: - type: adapter - local_adapter: issue_core.backends.gitea.backend.GiteaBackend - local_interface: issue_core.core.interfaces.RemoteBackend - reused_surface: Gitea /api/v1 issues, labels, milestones, comments - contracts: - - issue-core.backend.v1 - fragility_points: - - Gitea API field changes - - issue state mapping differences - - pagination and rate-limit behavior - - authentication token scopes - -validation: - harness: python3 -m pytest tests/test_gitea_backend.py - skip_without_runtime: true - checks: - - API client request shaping - - issue state mapping - - error handling for rate limits - policy: required-before-update - -update_policy: - default_action: require-maintainer-review - auto_eligible: false - -risks: - sensitivity: - - Gitea API breaking changes - - authentication model changes - - rate-limit policy changes - - license changes - escalation_triggers: - - validation failure - - Gitea major release - - production sync errors - -maintenance: - maintainers: - - issue-core - escalation_conditions: - - Gitea API compatibility failure - - validation failure - - production backend sync regression - -audit: - registered_at: "2026-06-24" - registered_by: open-reuse \ No newline at end of file diff --git a/issue_core/__init__.py b/issue_core/__init__.py index 9778f34..e9a0eee 100644 --- a/issue_core/__init__.py +++ b/issue_core/__init__.py @@ -19,6 +19,6 @@ Supported Backends: - Future: GitHub, GitLab, JIRA, Redmine """ -__version__ = "0.2.1" +__version__ = "0.2.0" __author__ = "Coulomb / MarkiTect Project" __description__ = "Authoritative task lifecycle manager with plugin architecture" diff --git a/issue_core/backends/gitea/backend.py b/issue_core/backends/gitea/backend.py index 4ed4b15..ed4887d 100644 --- a/issue_core/backends/gitea/backend.py +++ b/issue_core/backends/gitea/backend.py @@ -195,20 +195,10 @@ class GiteaBackend(RemoteBackend, SyncableBackend): if issue.milestone: data['milestone'] = int(issue.milestone.backend_id) if issue.milestone.backend_id else None - # Gitea expects numeric label IDs on issue create/update. Name-only - # labels are preserved in issue-core metadata but omitted from the API - # payload until a label-resolution step exists. - label_ids = [] - for label in issue.labels: - if not label.backend_id: - continue - try: - label_ids.append(int(label.backend_id)) - except (TypeError, ValueError): - continue - if label_ids: - data['labels'] = label_ids - + # Convert labels + if issue.labels: + data['labels'] = [label.name for label in issue.labels] + return data # Issue CRUD Operations diff --git a/k8s/railiance/configmap-backends.yaml b/k8s/railiance/configmap-backends.yaml index 3094f75..94b6372 100644 --- a/k8s/railiance/configmap-backends.yaml +++ b/k8s/railiance/configmap-backends.yaml @@ -17,7 +17,7 @@ data: "type": "gitea", "base_url": "http://gitea-http.default.svc.cluster.local:3000", "owner": "coulomb", - "repo": "markitect-main", + "repo": "markitect_project", "token": "__FROM_ENV__" }, "default": "markitect" diff --git a/k8s/railiance/deployment.yaml b/k8s/railiance/deployment.yaml index 88f8ccc..12d3a83 100644 --- a/k8s/railiance/deployment.yaml +++ b/k8s/railiance/deployment.yaml @@ -23,7 +23,7 @@ spec: # docs). Add imagePullSecrets: [{name: gitea-registry}] if it becomes private. containers: - name: issue-core - image: gitea.coulomb.social/coulomb/issue-core:0.2.1 + image: gitea.coulomb.social/coulomb/issue-core:0.2.0 imagePullPolicy: IfNotPresent ports: - name: http @@ -67,6 +67,5 @@ spec: allowPrivilegeEscalation: false readOnlyRootFilesystem: false runAsNonRoot: true - runAsUser: 10001 capabilities: drop: ["ALL"] diff --git a/tests/test_gitea_backend.py b/tests/test_gitea_backend.py index e3107c8..73df749 100644 --- a/tests/test_gitea_backend.py +++ b/tests/test_gitea_backend.py @@ -6,10 +6,8 @@ These tests ensure the Gitea backend works correctly with the API. import pytest import json -from datetime import datetime, timezone from unittest.mock import Mock, patch, MagicMock from issue_core.backends.gitea.backend import GiteaBackend, GiteaAPIError -from issue_core.core.models import Issue, IssueState, Label class TestGiteaBackend: @@ -98,29 +96,6 @@ class TestGiteaBackend: called_url = mock_request.call_args[1]['url'] if 'url' in mock_request.call_args[1] else mock_request.call_args[0][1] assert called_url == 'https://git.example.com/api/v1/repos/owner/repo' - def test_gitea_payload_omits_name_only_labels(self): - """Gitea issue payloads only include numeric label IDs.""" - now = datetime.now(timezone.utc) - issue = Issue( - id="", - number=0, - title="Test issue", - description="Test description", - state=IssueState.OPEN, - created_at=now, - updated_at=now, - labels=[ - Label(name="priority:low"), - Label(name="source:rule", backend_id="not-a-number"), - Label(name="existing", backend_id="42"), - ], - ) - - payload = self.backend._unified_issue_to_gitea(issue) - - assert payload["labels"] == [42] - assert payload["title"] == "Test issue" - @patch('issue_core.backends.gitea.backend.requests.Session') def test_test_connection_success(self, mock_session_class): """Test test_connection method works correctly.""" diff --git a/workplans/ISSUE-WP-0003-railiance01-deployment.md b/workplans/ISSUE-WP-0003-railiance01-deployment.md index 089c4e1..20d66a2 100644 --- a/workplans/ISSUE-WP-0003-railiance01-deployment.md +++ b/workplans/ISSUE-WP-0003-railiance01-deployment.md @@ -4,11 +4,11 @@ type: workplan title: "Deploy issue-core as a service on railiance01 (ArgoCD GitOps pilot)" domain: infotech repo: issue-core -status: finished +status: active owner: claude topic_slug: custodian created: "2026-06-19" -updated: "2026-06-30" +updated: "2026-06-23" state_hub_workstream_id: "896ace77-21b3-450b-8fb7-254aefc8c570" --- @@ -17,34 +17,36 @@ state_hub_workstream_id: "896ace77-21b3-450b-8fb7-254aefc8c570" `issue-core` is the authoritative task-lifecycle manager and the REST ingestion target for activity-core's `IssueSink`. Deployment artifacts are on `main` (`Dockerfile`, `docker-entrypoint.sh`, `k8s/railiance/`); image -`gitea.coulomb.social/coulomb/issue-core:0.2.1` is built, pushed, and -pullable. The railiance01 cluster now reconciles `issue-core` through ArgoCD; -External Secrets Operator reads the OpenBao-backed runtime Secret and the -Deployment is live on port 8765. +`gitea.coulomb.social/coulomb/issue-core:0.2.0` is built, pushed, and +pullable. The railiance01 cluster still has no `issue-core` workload until +T02 live ArgoCD bootstrap (RAILIANCE-WP-0004-T05) and T04 OpenBao secrets land. This workplan stands up `issue-core` as a first-class in-cluster service on railiance01 **via ArgoCD GitOps** — making issue-core the cluster's first declarative Application and turning on the idle GitOps capability. -## Current state (verified 2026-06-25) +## Current state (verified 2026-06-19) - **Deployment artifacts in-repo:** `Dockerfile`, `docker-entrypoint.sh`, and `k8s/railiance/` (Kustomize: ExternalSecret, ConfigMap, Deployment, Service). Image builds locally; `docker run` + `GET /healthz` returns 200. Image pushed - and pullable as `gitea.coulomb.social/coulomb/issue-core:0.2.1` (digest - `sha256:729c0e56…`). `coulomb` org packages are public — no `imagePullSecret` + and pullable as `gitea.coulomb.social/coulomb/issue-core:0.2.0` (digest + `sha256:153fbe43…`). `coulomb` org packages are public — no `imagePullSecret` required per `railiance-forge/docs/gitea-container-registry.md`. - **Dockerfile fix (2026-06-19):** build arg renamed `GITEA_PYPI_INDEX_URL` — `ARG PIP_INDEX_URL` leaked into the build env and pip used Gitea as the sole index, so dependencies like `click` were not found. -- **railiance01 cluster:** `issue-core` namespace, Service, ExternalSecret, - Secret, and Deployment are present. ArgoCD reports the `issue-core` Application - Synced/Healthy at revision `11a0a69`; pod is Ready on image `0.2.1`. -- **activity-core handoff still pending:** `activity-core/k8s/railiance/20-runtime.yaml` still points at port 8010 and keeps `ISSUE_SINK_TYPE: "null"`; T06 tracks switching it to the live issue-core service on port 8765. +- **railiance01 cluster:** no `issue-core` namespace; no issue-core + Deployment/Service/Pod in any namespace. +- **Dangling reference:** `activity-core/k8s/railiance/20-runtime.yaml` sets + `ISSUE_CORE_URL: http://issue-core.issue-core.svc.cluster.local:8010` — a + service that does not exist, on the **wrong port** (issue-core serves 8765) — + with `ISSUE_SINK_TYPE: "null"` so emission is disabled. It is a placeholder. - **Packaging precursor is done:** `ISSUE-WP-0002` published - `issue-core==0.2.0` to the Coulomb Gitea PyPI index. The live `0.2.1` image - was built from the committed source tree as a deployment hotfix. -- **ArgoCD is active for the pilot:** railiance-platform owns the bootstrap and tenant AppProject; `issue-core` is Synced/Healthy as the pilot workload. + `issue-core==0.2.0` to the Coulomb Gitea PyPI index. +- **ArgoCD is installed but unused:** all 7 components healthy (~290d), but + **0 Applications, 0 ApplicationSets, 0 registered git repos**, only the stock + `default` AppProject. No `kind: Application` manifests exist in any infra repo. - **Existing deploy pattern is imperative** (the path we are *replacing* for this service): local `docker build` → `k3s ctr images import` (side-load, no registry) → `rsync` manifests → `kubectl apply` (see @@ -59,37 +61,6 @@ declarative Application and turning on the idle GitOps capability. traceability string. Event-driven activity-core paths can still send UUIDs; scheduled/cron paths may now send a stable key such as `scheduled`. -## Live progress (2026-06-25) - -- Added railiance-platform ESO/OpenBao plumbing and provisioned the canonical - OpenBao path `platform/workloads/issue-core/issue-core/issue-core-runtime` - with `ISSUE_CORE_API_KEY` and `GITEA_BACKEND_TOKEN` (values not logged). -- Created dedicated Gitea service user `issue-core-svc` and stored a scoped - backend token in OpenBao for issue creation. -- Published and deployed `gitea.coulomb.social/coulomb/issue-core:0.2.1` - (`sha256:729c0e56…`) with the Gitea label-payload fix and numeric UID - securityContext. -- ArgoCD `issue-core` is Synced/Healthy at `11a0a69`; ExternalSecret is Ready; - `/healthz` returns 200; authenticated `POST /issues/` returned 201 and Gitea - issue id `175`. - -## Closeout recheck (2026-06-30) - -- issue-core-owned deployment work remains complete: manifests, runtime secret - contract, backend config, runbook, and direct authenticated ingestion smoke are - done for image `0.2.1`. -- The remaining completion gate is the activity-core producer handoff. A - non-secret source recheck of `/home/worsch/activity-core/k8s/railiance/20-runtime.yaml` - still shows `ISSUE_CORE_URL` on port `8010` and `ISSUE_SINK_TYPE: "null"`. -- `ops-warden` routing catalog entry `activity-core-issue-sink` confirms the - lane is owned by activity-core + issue-core and that ops-warden does not vend - `ISSUE_CORE_API_KEY`. -- This WSL session does not have `kubectl` on PATH, so live ArgoCD/Kubernetes - state could not be re-polled from the workstation. Keep T06/T07 at `wait` - until the activity-core runtime is switched to the service on port `8765`, - receives the shared key through OpenBao, and an activity-core emission returns - issue-core HTTP 201 with a created Gitea issue. - ## Decisions - **Deployment method = ArgoCD GitOps** (operator decision 2026-06-19). @@ -134,28 +105,30 @@ state_hub_task_id: "3723e896-3ec9-49b8-86f8-403993444da3" **Goal.** A reproducible, registry-hosted image ArgoCD-managed pods can pull. -- [x] Add `Dockerfile` building the checked-out `issue-core[api]` source. - Entrypoint renders `backends.json` then `issue serve --host 0.0.0.0 --port 8765`. +- [x] Add `Dockerfile` installing `issue-core[api]>=0.2,<0.3` from the Gitea + PyPI index (with explicit PyPI primary index). Entrypoint renders + `backends.json` then `issue serve --host 0.0.0.0 --port 8765`. - [x] Local build succeeds; `docker run` + `GET /healthz` returns 200. -- [x] Pushed `gitea.coulomb.social/coulomb/issue-core:0.2.1`; `docker pull` +- [x] Pushed `gitea.coulomb.social/coulomb/issue-core:0.2.0`; `docker pull` succeeds. - [x] No cluster pull secret needed (`coulomb` org packages are public). -- [x] `POST /issues/` smoke against a running deployment returned 201. +- [ ] `POST /issues/` smoke against a running deployment (deferred to T03/T04 + cluster verification). ## ArgoCD bootstrap (railiance-platform dependency) + issue-core Application ```task id: ISSUE-WP-0003-T02 -status: done +status: wait priority: high state_hub_task_id: "9b199b1d-d3c0-4621-b8f8-58c376cbf878" ``` **Owner split.** ArgoCD bootstrap is **railiance-platform's** (operator decision 2026-06-19): repo registration in ArgoCD, AppProject/app-of-apps -convention, and the agreed GitOps source layout. This handoff is complete for -the issue-core pilot; issue-core contributes workload manifests and platform owns -the tenant `Application` wrapper. +convention, and the agreed GitOps source layout. This task is `wait` on that +handoff. issue-core's part is to **contribute** the `Application` manifest + +workload manifests into the layout platform defines. - **(railiance-platform)** Register the GitOps source repo (repository Secret + creds); define AppProject for cluster services; publish the source-repo/path @@ -163,17 +136,16 @@ the tenant `Application` wrapper. - [x] **(issue-core)** Workload manifests in `k8s/railiance/` on `main` per platform contract (`docs/argocd-gitops.md`). Tenant `Application` lives in `railiance-platform/argocd/applications/issue-core.application.yaml`. -- [x] **(railiance-platform)** Live bootstrap deployed; `issue-core` Application - syncs from the issue-core repo through the tenant AppProject. -- [x] Verify: `kubectl get applications -n argocd` shows `issue-core` - Synced/Healthy at revision `11a0a69`; ArgoCD reconciled the `0.2.1` image - manifest change. +- [ ] **(railiance-platform)** RAILIANCE-WP-0004-T05 live bootstrap: register + repo creds, deploy bootstrap, sync `issue-core` Application. +- [ ] Verify: `kubectl get applications -n argocd` shows `issue-core` + Synced/Healthy; ArgoCD reconciles a trivial manifest change. ## Kubernetes manifests (namespace, Deployment, Service) in GitOps source ```task id: ISSUE-WP-0003-T03 -status: done +status: progress priority: high state_hub_task_id: "38887dd6-0988-4ad1-bc6b-2a1b8839829f" ``` @@ -182,18 +154,18 @@ state_hub_task_id: "38887dd6-0988-4ad1-bc6b-2a1b8839829f" - [x] `k8s/railiance/` Kustomize bundle (namespace via ArgoCD `CreateNamespace=true`). -- [x] Deployment: registry image tag `0.2.1`; port 8765; `/healthz` probes; +- [x] Deployment: registry image tag `0.2.0`; port 8765; `/healthz` probes; resource requests/limits; env from ExternalSecret (T04) and ConfigMap (T05). - [x] Service: ClusterIP on **8765** as `issue-core.issue-core.svc.cluster.local`. -- [x] Verify: ArgoCD syncs the manifests; pod Ready; `/healthz` returned 200 - from inside the cluster. +- [ ] Verify: ArgoCD syncs the manifests; Pod Ready; `/healthz` 200 from a debug + pod (blocked on T01 push + T02 bootstrap + T04 secrets). ## OpenBao secret: ISSUE_CORE_API_KEY ```task id: ISSUE-WP-0003-T04 -status: done +status: wait priority: high state_hub_task_id: "ad52527f-6222-4c11-9284-d8a3ed3b49ad" ``` @@ -208,15 +180,15 @@ state_hub_task_id: "ad52527f-6222-4c11-9284-d8a3ed3b49ad" - Never write the value to Git, manifests, State Hub, or logs. - Verify: both pods resolve a non-empty key; auth round-trip (401 without, 201 with). -- Done 2026-06-25: canonical OpenBao path exists, `ClusterSecretStore/openbao` is - Ready, `ExternalSecret/issue-core-runtime` is Ready, and the Kubernetes Secret - contains the two expected data keys. activity-core consumption remains in T06. +- Current wait reason: requires railiance-platform/OpenBao operator action to + confirm/provision the canonical path and `ClusterSecretStore`; + issue-core records only the Secret contract and non-secret verification steps. ## In-cluster backend config (cluster Gitea / markitect) ```task id: ISSUE-WP-0003-T05 -status: done +status: progress priority: medium state_hub_task_id: "10923f1e-050d-4f3e-980e-b061fef5f33a" ``` @@ -228,14 +200,14 @@ the cluster Gitea (markitect) backend. (`gitea-http.default.svc.cluster.local:3000`); token sentinel `__FROM_ENV__`. - [x] `docker-entrypoint.sh` renders `~/.config/issue-tracker/backends.json` from `BACKENDS_TEMPLATE` + `GITEA_BACKEND_TOKEN` at startup. -- [x] Verify: authenticated `POST /issues/` returned 201 and created Gitea - issue id `175` via the live service. +- [ ] Verify: a `POST /issues/` creates a real Gitea issue and returns + `issue_url` (blocked on T04 secrets + in-cluster deployment). ## Wire activity-core to the live service ```task id: ISSUE-WP-0003-T06 -status: done +status: progress priority: high state_hub_task_id: "96b14cdb-364f-4eab-a80e-dd8b3859c694" ``` @@ -251,10 +223,6 @@ state_hub_task_id: "96b14cdb-364f-4eab-a80e-dd8b3859c694" accepts `triggering_event_id` as a non-empty traceability string, so event-driven paths can send UUIDs and cron paths can send stable keys such as `"scheduled"`. -- [ ] activity-core runtime source still needs the live flip: - `ISSUE_CORE_URL` `8010 -> 8765` and `ISSUE_SINK_TYPE` `"null" -> "rest"`. -- [ ] activity-core worker still needs the shared `ISSUE_CORE_API_KEY` from the - approved OpenBao lane; never write the value to Git, State Hub, logs, or chat. - Verify: an activity-core run emits a task that lands in cluster Gitea via issue-core. @@ -262,7 +230,7 @@ state_hub_task_id: "96b14cdb-364f-4eab-a80e-dd8b3859c694" ```task id: ISSUE-WP-0003-T07 -status: done +status: progress priority: medium state_hub_task_id: "8d853b8e-cfca-441d-b817-0a29e37bd66e" ``` @@ -271,12 +239,10 @@ state_hub_task_id: "8d853b8e-cfca-441d-b817-0a29e37bd66e" - ArgoCD Application Synced/Healthy; issue-core Pod Ready; Service reachable cluster-internal. -- [x] activity-core -> issue-core emission returns 201 and creates a Gitea issue - (2026-07-02: Gitea issue `176` via the live sink path — see completion note). +- activity-core → issue-core emission returns 201 and creates a Gitea issue. - [x] Document the GitOps runbook (image build/push, ArgoCD sync, secret contract, smoke, activity-core handoff) in `docs/argocd-gitops.md`. -- Emit an `add_progress_event` milestone to the hub when the activity-core - emission proof exists and this workplan can move from `blocked` to `finished`. +- Emit an `add_progress_event` milestone to the hub on completion. --- @@ -289,39 +255,3 @@ state_hub_task_id: "8d853b8e-cfca-441d-b817-0a29e37bd66e" - `activity-core/k8s/railiance/README.md` — the imperative pattern being superseded for this service. - `~/ops-warden/wiki/playbooks/activity-core-issue-sink.md` — key routing. - - -## Completion 2026-07-02 — live emission proven, topology corrected - -**Topology correction:** this workplan's "railiance01 cluster" is actually the -CoulombCore k3s cluster (92.205.130.254, reached via the workstation -kubeconfig tunnel `127.0.0.1:16443`). The real railiance01 (92.205.62.239) -hosts activity-core and has no issue-core namespace. The in-cluster -`issue-core.issue-core.svc.cluster.local` URL that T06 originally assumed was -therefore never resolvable from activity-core. - -**Cross-machine lane built (2026-07-02):** -- ops-bridge gained an optional `remote_host` forward destination - (ops-bridge commit) enabling tunnels to k3s ClusterIPs. -- Tunnels: `issue-core-coulombcore` (workstation `127.0.0.1:18765` -> - CoulombCore ClusterIP `10.43.103.154:8765`, health-checked on `/healthz`) - and `issue-core-railiance01` (railiance01 `127.0.0.1:18765` -> workstation). -- activity-core commit `a1e2a42`: new `actcore-issue-core-bridge` - hostNetwork proxy (host port 18081 -> node-local 18765) cloned from the - state-hub-bridge pattern, `ISSUE_CORE_URL` -> - `http://actcore-issue-core-bridge.activity-core.svc.cluster.local:8765`, - `ISSUE_SINK_TYPE` -> `rest`. -- `ISSUE_CORE_API_KEY` merged into railiance01's `actcore-runtime-secret` by - the operator via a stdin-only pipe from the approved OpenBao lane - (`CCR-2026-0002`, activated the same day). - -**Emission proof:** from inside the restarted actcore-worker, the real -`IssueCoreRestSink` emitted a labeled smoke TaskSpec and received -`TaskRef(external_id='176', backend='gitea')` — Gitea issue `176` in -`coulomb/markitect-main` (default backend). Auth, bridge, tunnels, issue-core -validation, and Gitea creation all exercised on the production path. - -**Contract note for emitters:** `POST /issues/` requires `target_repo` and -`activity_definition_id` (422 otherwise). activity-core `TaskSpec` defaults -(`target_repo=None`, `activity_definition_id=""`) will be rejected — rule and -instruction emitters must populate both.