CI Pipelines (Subsystem A) Implementation Plan¶
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Give each of the five tatara Go service repos a GitHub Actions pipeline (secscan/lint/test/build/smoke on PR; same plus a kaniko image build+push to harbor on push-to-main), running on the org-scoped self-hosted ARC runner.
Architecture: Per-repo .github/workflows/ci.yml + one .github/ci/kaniko-build.sh that, on main only, dispatches a kaniko Kubernetes Job (kaniko self-clones the private repo via git context and pushes to harbor.szymonrichert.pl/containers/<repo>:<sha> and :<git-describe-version>). Infra prerequisites (org-scope the runner; a tatara-ci namespace + dispatcher SA/RBAC + a sops harbor docker-config secret) ship via the infra helmfile. Spec: docs/superpowers/specs/2026-06-12-ci-pipelines-design.md.
Tech Stack: GitHub Actions, ARC (actions-runner-controller), kaniko, kubectl, Go (setup-go/go.mod), golangci-lint v2.1.6, gitleaks v8.30.1, helmfile + sops.
As-built amendment (2026-06-12): harbor push auth now comes from the repo
HARBOR_USERNAME/HARBOR_PASSWORDGitHub secrets (already set on all 5 repos), materialized at dispatch as a transient docker-registry secret - so U2 and the persistentharbor-push-dockercfgsops secret are eliminated, and Task 8's MR dropped the secret (just ns + SA/RBAC + serviceAccountName, already merged ashelmfile 3472e24f). The kaniko Job is a heredoc insidekaniko-build.sh(no separatekaniko-job.yaml/ envsubst). The authoritative as-built description is the spec's "As-built status" + "Image job" sections. U1 is the only remaining user step. Seedocs/superpowers/specs/2026-06-12-ci-pipelines-design.md.
One irreducible user step (blocker - flag, do not work around)¶
- U1 (gates Task 9 / MR-B): Grant the existing
szymonrychu-arcGitHub App access to the orgszymonrychu(or at least the 5 component repos) with self-hosted-runner administration. Until done, org-level runner registration fails. ThegithubConfigUrlorg change (Task 9) MUST NOT be merged before U1, or the existingtatararunner deregisters and breaks.
PR pipelines (secscan/lint/test/build/smoke) and the image job both just need U1 (harbor creds are GitHub secrets, already provided).
File structure¶
Per component repo (tatara-operator, tatara-cli, tatara-memory, tatara-memory-repo-ingester, tatara-claude-code-wrapper): - Create .github/workflows/ci.yml - the pipeline (smoke job present only on cli/memory/wrapper). - Create .github/ci/kaniko-build.sh - main-only kaniko Job dispatcher (identical across repos).
Infra repo (~/Documents/infra/helmfile), all under helmfiles/coding/values/arc-runner-szymonrychu/: - Modify common.yaml - add serviceAccountName (MR-A); change githubConfigUrl to org (MR-B, gated on U1). - Create raw/tatara-ci-namespace.common.pre.yaml - Namespace tatara-ci. - Create raw/tatara-ci-dispatcher.common.pre.yaml - ServiceAccount tatara-ci-dispatcher (ns arc-runners) + Role + RoleBinding (ns tatara-ci). - Create raw/harbor-push-dockercfg.tatara-ci.pre.secrets.yaml - sops Secret (dockerconfigjson) in tatara-ci.
Phasing¶
- Phase 1 (Tasks 1-3): Build the operator pipeline + dispatcher, validate locally (actionlint/shellcheck/dry-run). Operator is the template-proving repo.
- Phase 2 (Tasks 4-7): Replicate to cli, memory, ingester, wrapper with per-repo deltas (parallelizable subagents).
- Phase 3 (Tasks 8-9): Infra MR-A (namespace/SA/RBAC/secret scaffold + serviceAccountName) and MR-B (org-scope, gated on U1).
- Phase 4 (Task 10): End-to-end verification on real infra after U1+U2.
Branch flow per repo: worktree off freshly-pulled main (per the dev-from-fresh-main rule - the autonomous bots push to these repos), land via PR. Infra ships via GitLab MR (auto-merge on pipeline).
Task 1: Operator kaniko dispatcher script¶
Files: - Create: tatara-operator/.github/ci/kaniko-build.sh
- Step 1: Start from fresh main
cd ~/Documents/tatara/tatara-operator
git checkout main && git pull --ff-only
git checkout -b ci/pipelines
mkdir -p .github/ci .github/workflows
- Step 2: Write the dispatcher script
Create .github/ci/kaniko-build.sh:
#!/usr/bin/env bash
# Dispatch a one-shot kaniko Job that builds this repo's image and pushes it to
# harbor. Runs on the ARC runner (in-cluster); uses the runner's mounted
# ServiceAccount for kubectl. Streams kaniko logs and propagates the Job result.
set -euo pipefail
REPO="${1:?repo name required}" # e.g. tatara-operator
NS="tatara-ci"
SHORT_SHA="${GITHUB_SHA:0:7}"
VERSION="$(git describe --tags --always --dirty)"
BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
JOB="kaniko-${REPO}-${SHORT_SHA}"
CLONE_SECRET="clone-${REPO}-${SHORT_SHA}"
cleanup() {
kubectl -n "$NS" delete secret "$CLONE_SECRET" --ignore-not-found >/dev/null 2>&1 || true
}
trap cleanup EXIT
# Transient clone-token secret (kaniko git-context auth for the private repo).
# Token never printed; created via stdin apply.
kubectl -n "$NS" create secret generic "$CLONE_SECRET" \
--from-literal=username=x-access-token \
--from-literal=token="${GITHUB_TOKEN:?GITHUB_TOKEN required}" \
--dry-run=client -o yaml | kubectl apply -f - >/dev/null
# Render and apply the kaniko Job.
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: ${JOB}
namespace: ${NS}
spec:
backoffLimit: 0
ttlSecondsAfterFinished: 600
activeDeadlineSeconds: 1500
template:
spec:
restartPolicy: Never
imagePullSecrets:
- name: regcred
containers:
- name: kaniko
image: harbor.szymonrichert.pl/containers/kaniko-executor:v1.24.0-debug
command: ["/kaniko/executor"]
args:
- --context=git://github.com/szymonrychu/${REPO}.git#${GITHUB_SHA}
- --dockerfile=Dockerfile
- --destination=harbor.szymonrichert.pl/containers/${REPO}:${SHORT_SHA}
- --destination=harbor.szymonrichert.pl/containers/${REPO}:${VERSION}
- --build-arg=VERSION=${VERSION}
- --build-arg=COMMIT=${SHORT_SHA}
- --build-arg=DATE=${BUILD_DATE}
- --compressed-caching=false
- --cache-copy-layers=true
env:
- name: GIT_USERNAME
valueFrom:
secretKeyRef: { name: ${CLONE_SECRET}, key: username }
- name: GIT_PASSWORD
valueFrom:
secretKeyRef: { name: ${CLONE_SECRET}, key: token }
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker
volumes:
- name: docker-config
secret:
secretName: harbor-push-dockercfg
items:
- { key: .dockerconfigjson, path: config.json }
EOF
# Wait for the pod, stream kaniko logs to completion, then read the Job result.
for _ in $(seq 1 60); do
if kubectl -n "$NS" get pod -l job-name="$JOB" -o name 2>/dev/null | grep -q .; then break; fi
sleep 2
done
kubectl -n "$NS" logs -f "job/${JOB}" || true
for _ in $(seq 1 30); do
succeeded="$(kubectl -n "$NS" get job "$JOB" -o jsonpath='{.status.succeeded}' 2>/dev/null || true)"
failed="$(kubectl -n "$NS" get job "$JOB" -o jsonpath='{.status.failed}' 2>/dev/null || true)"
if [[ "$succeeded" == "1" ]]; then echo "kaniko: build pushed"; exit 0; fi
if [[ -n "$failed" && "$failed" != "0" ]]; then echo "kaniko: build failed"; exit 1; fi
sleep 2
done
echo "kaniko: timed out waiting for Job result"
exit 1
- Step 3: Make it executable + shellcheck
Expected: shellcheck exits 0 (no findings). If shellcheck is absent: mise use -g shellcheck@latest or brew install shellcheck.
- Step 4: Commit
Task 2: Operator ci.yml workflow¶
Files: - Create: tatara-operator/.github/workflows/ci.yml
Operator has no smoke job (needs a live cluster) and needs committed generated code (present in-repo; make lint/test/build run standalone). make test downloads envtest assets via go run setup-envtest (network available on the runner).
- Step 1: Write the workflow
Create .github/workflows/ci.yml:
name: ci
on:
pull_request:
branches: [main]
push:
branches: [main]
jobs:
secscan:
runs-on: szymonrychu-tatara
steps:
- uses: actions/checkout@v4
- name: gitleaks
run: |
curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_x64.tar.gz" \
| tar -xz gitleaks
./gitleaks detect --source . --no-git --redact --verbose
lint:
runs-on: szymonrychu-tatara
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: gofmt
run: test -z "$(gofmt -s -l .)" || { gofmt -s -l .; echo 'gofmt -s required'; exit 1; }
- name: golangci-lint
run: |
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh \
| sh -s -- -b "$(go env GOPATH)/bin" v2.1.6
export PATH="$(go env GOPATH)/bin:$PATH"
make lint
test:
runs-on: szymonrychu-tatara
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: test
run: make test
build:
runs-on: szymonrychu-tatara
needs: [secscan, lint, test]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: build
run: make build
image:
if: ${{ github.event_name == 'push' }}
runs-on: szymonrychu-tatara
needs: [secscan, lint, test, build]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: azure/setup-kubectl@v4
- name: build and push image
run: bash .github/ci/kaniko-build.sh "${{ github.event.repository.name }}"
- Step 2: actionlint the workflow
cd ~/Documents/tatara/tatara-operator
curl -sSfL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash | bash -s -- latest /tmp 2>/dev/null
/tmp/actionlint .github/workflows/ci.yml
Expected: actionlint exits 0. (Shellcheck of inline run: scripts is included by actionlint.) Fix any reported issue before committing.
- Step 3: Commit
git add .github/workflows/ci.yml
git commit -m "ci: add GitHub Actions pipeline (secscan/lint/test/build + main image)"
Task 3: Operator PR + finish branch¶
Files: none (integration step)
- Step 1: Push the branch and open a PR
cd ~/Documents/tatara/tatara-operator
git push -u origin ci/pipelines
gh pr create --title "ci: GitHub Actions pipeline + kaniko image build" \
--body "Subsystem A pipeline: secscan/lint/test/build on PR; kaniko build+push to harbor on main. Requires infra MR-A/B + App grant + harbor secret (see plan 2026-06-12-ci-pipelines)."
- Step 2: Note - do not expect green until infra is live
The PR will register a ci check. Until U1 (App grant) + the infra MR-A merge, jobs queue with no runner. This is expected; verification happens in Task 10. Leave the PR open.
Task 4: tatara-cli pipeline (has smoke)¶
Files: - Create: tatara-cli/.github/ci/kaniko-build.sh (identical to Task 1 Step 2) - Create: tatara-cli/.github/workflows/ci.yml
- Step 1: Fresh main + branch + copy dispatcher
cd ~/Documents/tatara/tatara-cli
git checkout main && git pull --ff-only
git checkout -b ci/pipelines
mkdir -p .github/ci .github/workflows
cp ~/Documents/tatara/tatara-operator/.github/ci/kaniko-build.sh .github/ci/kaniko-build.sh
chmod +x .github/ci/kaniko-build.sh
- Step 2: Write ci.yml (with smoke)
Identical to Task 2 Step 1, but insert a smoke job and add it to build/image needs. The full smoke job:
smoke:
runs-on: szymonrychu-tatara
needs: [build]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version-file: go.mod
cache: true
- name: smoke
run: |
make build
./bin/tatara --help
Change build.needs to [secscan, lint, test] (unchanged) and image.needs to [secscan, lint, test, build, smoke]. Insert the smoke job after build.
- Step 3: Validate
Expected: both exit 0.
- Step 4: Commit, push, PR
git add .github/ci/kaniko-build.sh .github/workflows/ci.yml
git commit -m "ci: add GitHub Actions pipeline (secscan/lint/test/build/smoke + main image)"
git push -u origin ci/pipelines
gh pr create --title "ci: GitHub Actions pipeline + kaniko image build" \
--body "Subsystem A pipeline. Requires infra prerequisites (see plan 2026-06-12-ci-pipelines)."
Task 5: tatara-memory pipeline (has smoke, health-curl)¶
Files: - Create: tatara-memory/.github/ci/kaniko-build.sh (identical to Task 1 Step 2) - Create: tatara-memory/.github/workflows/ci.yml
-
Step 1: Fresh main + branch + copy dispatcher (as Task 4 Step 1, in
~/Documents/tatara/tatara-memory). -
Step 2: Write ci.yml with the memory smoke job
As Task 4 Step 2, but the smoke job step boots the server and probes /healthz (handler in cmd/tatara-memory/health.go always returns 200):
- name: smoke
run: |
make build
./bin/tatara-memory &
PID=$!
trap 'kill $PID 2>/dev/null || true' EXIT
for i in $(seq 1 20); do
if curl -fsS http://127.0.0.1:8080/healthz >/dev/null 2>&1; then echo healthy; exit 0; fi
sleep 1
done
echo "memory did not become healthy"; exit 1
Implementer check: read cmd/tatara-memory/main.go to confirm (a) the default listen address/port (replace 8080 if different) and (b) that the server binds and serves /healthz without a configured backend. If main hard-requires a backend to start, downgrade the smoke to ./bin/tatara-memory --help (exit 0) and note the downgrade in the PR body.
-
Step 3: Validate (shellcheck + actionlint, both exit 0).
-
Step 4: Commit, push, PR (as Task 4 Step 4).
Task 6: tatara-memory-repo-ingester pipeline (no smoke, CGO)¶
Files: - Create: tatara-memory-repo-ingester/.github/ci/kaniko-build.sh (identical to Task 1 Step 2) - Create: tatara-memory-repo-ingester/.github/workflows/ci.yml
Ingester builds/tests with CGO_ENABLED=1 (needs a C compiler) and has no smoke (needs a memory backend).
-
Step 1: Fresh main + branch + copy dispatcher (as Task 4 Step 1, in
~/Documents/tatara/tatara-memory-repo-ingester). -
Step 2: Write ci.yml = Task 2's operator workflow (no smoke), but add a gcc install step to the
testandbuildjobs (CGO):
In both test and build jobs, before the make step:
- name: install gcc (CGO)
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends gcc
-
Step 3: Validate (shellcheck + actionlint, both exit 0).
-
Step 4: Commit, push, PR (as Task 4 Step 4).
Task 7: tatara-claude-code-wrapper pipeline (has smoke)¶
Files: - Create: tatara-claude-code-wrapper/.github/ci/kaniko-build.sh (identical to Task 1 Step 2) - Create: tatara-claude-code-wrapper/.github/workflows/ci.yml
-
Step 1: Fresh main + branch + copy dispatcher (as Task 4 Step 1, in
~/Documents/tatara/tatara-claude-code-wrapper). -
Step 2: Write ci.yml = Task 4 (with smoke), smoke step:
-
Step 3: Validate (shellcheck + actionlint, both exit 0).
-
Step 4: Commit, push, PR (as Task 4 Step 4).
Task 8: Infra MR-A - tatara-ci namespace, dispatcher SA/RBAC, harbor secret, serviceAccountName¶
Files (all under ~/Documents/infra/helmfile/helmfiles/coding/values/arc-runner-szymonrychu/): - Modify: common.yaml (add serviceAccountName only - NOT the configUrl yet) - Create: raw/tatara-ci-namespace.common.pre.yaml - Create: raw/tatara-ci-dispatcher.common.pre.yaml - Create: raw/harbor-push-dockercfg.tatara-ci.pre.secrets.yaml (sops)
- Step 1: Fresh main + branch
cd ~/Documents/infra/helmfile
git checkout main && git pull --ff-only
git checkout -b feat/tatara-ci-kaniko-dispatcher
- Step 2: Namespace raw manifest
Create raw/tatara-ci-namespace.common.pre.yaml:
- Step 3: Dispatcher SA + RBAC raw manifest
Create raw/tatara-ci-dispatcher.common.pre.yaml:
apiVersion: v1
kind: ServiceAccount
metadata:
name: tatara-ci-dispatcher
namespace: arc-runners
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: tatara-ci-dispatcher
namespace: tatara-ci
rules:
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["create", "get", "list", "watch", "delete"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["create", "get", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: tatara-ci-dispatcher
namespace: tatara-ci
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: tatara-ci-dispatcher
subjects:
- kind: ServiceAccount
name: tatara-ci-dispatcher
namespace: arc-runners
- Step 4: Harbor docker-config sops secret scaffold
Create the plaintext skeleton, then encrypt. Create raw/harbor-push-dockercfg.tatara-ci.pre.secrets.yaml:
apiVersion: v1
kind: Secret
metadata:
name: harbor-push-dockercfg
namespace: tatara-ci
type: kubernetes.io/dockerconfigjson
stringData:
.dockerconfigjson: |
{"auths":{"harbor.szymonrichert.pl":{"username":"REPLACE","password":"REPLACE"},"https://index.docker.io/v1/":{"username":"REPLACE","password":"REPLACE"}}}
Then encrypt in place:
cd ~/Documents/infra/helmfile
sops -e -i helmfiles/coding/values/arc-runner-szymonrychu/raw/harbor-push-dockercfg.tatara-ci.pre.secrets.yaml
U2 (user step): the user sets the real HARBOR + DOCKERHUB creds. Tell the user to run (interactive):
cd ~/Documents/infra/helmfile
sops helmfiles/coding/values/arc-runner-szymonrychu/raw/harbor-push-dockercfg.tatara-ci.pre.secrets.yaml
and replace the four REPLACE values with the push-capable HARBOR robot creds and a DOCKERHUB account (same creds optimizarr uses). Verify the file still matches the .sops.yaml rule (.*.secret.*.yaml).
- Step 5: Add serviceAccountName to the runner template
In common.yaml, under template.spec, add (do NOT change githubConfigUrl in this MR):
- Step 6: Validate the helmfile renders
cd ~/Documents/infra/helmfile
helmfile -e coding -l application=arc-controller deps >/dev/null 2>&1 || true
helmfile -e coding diff -l name=arc-runner-szymonrychu 2>&1 | head -60
Expected: diff shows the added serviceAccountName; the raw/ manifests are applied by the presync .hook.sh (not in the helm diff). Confirm no template errors. Verify the tatara-ci-dispatcher SA lives in arc-runners (runner pods' namespace) so the runner template can reference it.
- Step 7: Commit + MR (auto-merge on pipeline)
cd ~/Documents/infra/helmfile
git add helmfiles/coding/values/arc-runner-szymonrychu/
pre-commit run --all-files || git add -A
git commit -m "feat(arc): tatara-ci namespace + kaniko dispatcher SA/RBAC + harbor push secret"
git push -u origin feat/tatara-ci-kaniko-dispatcher \
-o merge_request.create \
-o merge_request.title="feat(arc): tatara-ci kaniko dispatcher" \
-o merge_request.merge_when_pipeline_succeeds \
-o merge_request.remove_source_branch
Then monitor the pipeline (glab ci status) until merged, and git checkout main && git pull.
Task 9: Infra MR-B - org-scope the runner (GATED on U1)¶
Files: - Modify: ~/Documents/infra/helmfile/helmfiles/coding/values/arc-runner-szymonrychu/common.yaml
DO NOT START until U1 is confirmed (the szymonrychu-arc App is granted org / the 5 repos). Merging this before U1 deregisters the existing runner and breaks tatara CI.
-
Step 1: Confirm U1 with the user. Ask the user to confirm the App now has access to the org (or the 5 component repos). Do not proceed otherwise.
-
Step 2: Fresh main + branch
cd ~/Documents/infra/helmfile
git checkout main && git pull --ff-only
git checkout -b feat/arc-org-scope
- Step 3: Change githubConfigUrl to org
In common.yaml:
- Step 4: Validate render
Expected: diff shows the configUrl change only.
- Step 5: Commit + MR (auto-merge on pipeline)
git add helmfiles/coding/values/arc-runner-szymonrychu/common.yaml
pre-commit run --all-files || git add -A
git commit -m "feat(arc): org-scope szymonrychu-tatara runner for all tatara repos"
git push -u origin feat/arc-org-scope \
-o merge_request.create \
-o merge_request.title="feat(arc): org-scope runner" \
-o merge_request.merge_when_pipeline_succeeds \
-o merge_request.remove_source_branch
Monitor until merged; git checkout main && git pull.
- Step 6: Confirm runner registration
kubectl -n arc-runners get autoscalingrunnerset,ephemeralrunnerset 2>/dev/null
kubectl -n arc-runners get pods
Expected: the runner scale set reports healthy against the org; no registration errors in kubectl -n arc-runners logs deploy/arc-controller-gha-rs-controller (or the controller pod).
Task 10: End-to-end verification (after U1 + U2 + MR-A + MR-B)¶
Files: none (verification)
- Step 1: Trigger the operator PR pipeline
Push an empty commit (or re-run) on the operator ci/pipelines PR branch so the workflow schedules on the now-available runner:
cd ~/Documents/tatara/tatara-operator
git commit --allow-empty -m "ci: trigger pipeline"
git push
gh run watch "$(gh run list -R szymonrychu/tatara-operator --branch ci/pipelines -L1 --json databaseId -q '.[0].databaseId')" --exit-status
Expected: secscan, lint, test, build all succeed; image is skipped (PR event). If a job fails, debug via superpowers:systematic-debugging (read the run logs; do not guess).
- Step 2: Merge operator PR and verify the main pipeline + harbor image
gh pr merge -R szymonrychu/tatara-operator --squash --delete-branch <pr-number>
# watch the main run
gh run watch "$(gh run list -R szymonrychu/tatara-operator --branch main -L1 --json databaseId -q '.[0].databaseId')" --exit-status
Expected: main run runs image; kaniko Job logs stream; the run concludes success. Confirm the harbor tags exist:
SHA=$(git -C ~/Documents/tatara/tatara-operator rev-parse --short=7 main)
curl -fsS -u "$HARBOR_USERNAME:$HARBOR_PASSWORD" \
"https://harbor.szymonrichert.pl/v2/containers/tatara-operator/tags/list" | grep -o "$SHA"
Expected: the short-SHA tag is listed (plus the git-describe version tag).
- Step 3: Confirm the
cicheck on the main commit (subsystem B contract)
gh api "repos/szymonrychu/tatara-operator/commits/main/check-runs" \
-q '.check_runs[] | select(.name=="ci") | {name, conclusion}'
Expected: a check-run named ci with conclusion: success on the main HEAD. This is the green signal subsystem B consumes.
- Step 4: Roll out the other 4 PRs
Merge each of the cli/memory/ingester/wrapper PRs once their PR pipelines are green (repeat Steps 1-3 per repo). Verify each produces its harbor image and a green ci check on main.
- Step 5: Update MEMORY.md / ROADMAP.md
In ~/Documents/tatara: note in MEMORY.md that the 5 Go repos now have GitHub Actions CI (kaniko-on-ARC, harbor push) and that the ci check on main is the subsystem-B merge-green contract. Move the CI-pipelines item out of ROADMAP.md. Note subsystem B (operator wait/conflict/close) is the next phase.
Self-review notes¶
- Spec coverage: secscan(gitleaks)=Task2/secscan; static lint+gofmt=lint job; tests=test job; build gate=build job; smoke matrix=Tasks ⅘/7 (cli/memory/wrapper) + none for operator/ingester (Tasks 2/6); kaniko image+harbor push on main=Task1 script + image job; org-scope runner=Task9; tatara-ci/SA/RBAC/secret=Task8; subsystem-B contract (
cicheck on main)=Task10 Step3. All spec sections covered. - Per-repo deltas captured: operator (no smoke, envtest in
make test); ingester (no smoke, CGO gcc install); cli/wrapper (--helpsmoke); memory (health-curl smoke with--helpfallback). - Ordering hazard encoded: MR-A (Task8) safe anytime; MR-B (Task9) hard-gated on U1; image job green needs U2.
- No unit tests (CI config); verification gates are shellcheck + actionlint + helmfile diff + end-to-end pipeline runs, stated per task.