Review: Add prediction-assistant to services terraform

review-1767-2026-07-04 Review

review ready

Verdict: READY

Pass 3 — 2026-07-04. Prior passes had conflicting findings about whether the entry already exists. Verified directly: it does NOT exist in k3s.tfvars. Issue body corrected to say "add new entry".

Template Completeness

  • [x] Type (Feature)
  • [x] Lineage
  • [x] Repo
  • [x] User Story
  • [x] Context
  • [x] File Targets
  • [x] Feature Flag (none)
  • [x] Acceptance Criteria
  • [x] Test Expectations
  • [x] Constraints
  • [x] Checklist
  • [x] Related

Traceability

  • [x] story:platform-setup label — verified in project-prediction-assistant user-stories table
  • [x] story note verified — "Platform Setup" entry exists on project-prediction-assistant user-stories section (key: platform-setup, role: Developer (Lucas))
  • [x] arch:iac label — present on board item
  • [ ] arch note MISSING — arch-iac note does not exist in pal-e-docs. Known platform-wide documentation gap; not blocking per review policy
  • [x] Forgejo issue — ldraney/pal-e-services#170, open

File Targets

  • [x] terraform/k3s.tfvars — verified: file exists (symlinked from ~/secrets/pal-e-services/k3s.tfvars). Confirmed prediction-assistant entry does NOT exist (grep returns empty). Services map starts at line 218. Existing pattern clear (landscaping-assistant at line 228 is the closest Rails analog: port 3000, funnel true).
  • [x] terraform/services.tf — verified: for_each loop at line 156. coalesce(each.value.source_repo, each.value.forgejo_repo) at line 181 correctly handles null source_repo by falling back to forgejo_repo. Agent correctly told NOT to touch.
  • [x] terraform/variables.tf — verified: service type definition at lines 234-247. All required fields present (forgejo_repo, image_repo, port, funnel, source_repo optional, source_path optional). Agent correctly told NOT to touch.
  • [x] k8s/overlays/prod/ — verified: directory exists in prediction-assistant repo with kustomization.yaml, ingress.yaml, cluster-issuer.yaml, redirect-middleware.yaml. source_path value "k8s/overlays/prod" is accurate.

Repo Placement

Correct. Issue filed on pal-e-services (#170), which is where the services terraform and k3s.tfvars live. Single-repo change — no cross-repo coordination needed.

Dependencies

  • #1769 "Add prediction-assistant database to CNPG" (backlog, sprint:9) — sibling, not a blocker. Services entry is independent of database provisioning.
  • #1768 "Add Keycloak realm + OIDC client" (backlog, sprint:9) — sibling, not a blocker. Keycloak and services are separate terraform resources.
  • #1772 "tofu apply — provision prediction-assistant infra" (backlog, sprint:10) — downstream dependency. Correctly sequenced one sprint later. Depends on this ticket completing first.
  • #1770 "Update .woodpecker.yaml to match platform CI pattern" (backlog, sprint:9) — sibling, independent.
No undocumented blockers. Sprint sequencing (sprint:9 before sprint:10) implicitly handles the dependency chain.

Acceptance Criteria

All 4 criteria are concrete and agent-verifiable:
  • [x] AC1: "prediction-assistant entry exists in services map without source_repo" — verifiable via grep. Omitting source_repo is a supported pattern: coalesce() falls back to forgejo_repo (line 181 of services.tf). Precedent: platform-validation entry in k3s.tfvars.example also omits source_repo.
  • [x] AC2: "source_path is k8s/overlays/prod" — verifiable via grep. Path confirmed to exist in the prediction-assistant repo with kustomization.yaml.
  • [x] AC3: "tofu plan shows new namespace, Harbor project, robot accounts, ArgoCD application, Tailscale funnel" — verifiable via tofu plan output.
  • [x] AC4: "No errors on plan" — verifiable via exit code.
Test command cd terraform && tofu plan is real and appropriate.

Blast Radius

Low. Adding a new entry to the services map is additive — it cannot break existing services. The for_each loop processes each service independently. The image-updater annotation conditional (lines 171-173 of services.tf) correctly omits the kustomization write-back-target when source_repo is null, which is appropriate for a service whose k8s manifests live in the app repo. No similar bug pattern to propagate — this is a new entry, not a fix.

Decomposition Assessment

  • 1 file target across 1 repo — below threshold
  • 4 acceptance criteria — below threshold
  • Estimated agent work: under 2 minutes (add ~8 lines to a tfvars map, run tofu plan)
No decomposition needed.

Recommendation

  • [SCOPE] Create architecture note arch-iac for the IaC component — platform-wide gap, not blocking this ticket. Track as a separate documentation item.
No other action needed. Scope is solid.