Review: Add Woodpecker CI pipeline and k8s manifests (gcal-mcp-remote) — 3rd pass

review-63-2026-03-27c Review

review ready

Verdict: READY

Third review pass after two rounds of refinement. All 7 body fixes from review-63-2026-03-27b applied. Stale PR#4 decision resolved ("close and start fresh"). Issue body is now complete and accurate.

Template Completeness

  • [x] Type — Feature
  • [x] Lineage — plan-2026-02-25-mcp-gateway-migration Phase 3
  • [x] Repo — gcal-mcp-remote
  • [x] User Story — platform operator needs standardized CI/CD pipeline
  • [x] Context — monolith split, reference pattern, src/ layout caveat
  • [x] File Targets — 7 create, 4 modify, 1 NOT touch boundary
  • [x] Acceptance Criteria — 3 criteria (CI build, ArgoCD deploy, /metrics)
  • [x] Test Expectations — 6 items with concrete commands
  • [x] Constraints — Harbor project, Woodpecker secrets, ArgoCD app, src/ layout, stale PR#4
  • [x] Checklist — present
  • [x] Related — project-pal-e + notion-mcp-remote reference

Traceability

  • [x] story:superuser-onboard — platform operator onboarding MCP services
  • [x] arch:ci-pipeline — CI/CD infrastructure component
  • [x] Forgejo issue — forgejo_admin/gcal-mcp-remote#3, open

File Targets

Files to create (7):
  • [x] .woodpecker.yaml — does not exist, correct (reference: notion-mcp-remote/.woodpecker.yaml)
  • [x] Dockerfile.k8s — does not exist, correct. Issue explicitly notes src/ layout difference from reference
  • [x] k8s/deployment.yaml — does not exist, correct
  • [x] k8s/service.yaml — does not exist, correct
  • [x] k8s/pvc.yaml — does not exist, correct
  • [x] k8s/servicemonitor.yaml — does not exist, correct
  • [x] k8s/kustomization.yaml — does not exist, correct
Files to modify (4):
  • [x] src/gcal_mcp_remote/server.py — EXISTS. Currently has config, OAuth setup. Needs /metrics endpoint addition
  • [x] pyproject.toml — EXISTS. Uses src/ layout with [tool.setuptools.packages.find] where=["src"]. No ruff config or dev deps yet
  • [x] systemd/gcal-mcp-remote.service — EXISTS. ExecStart points to server.py, no explicit PORT env var (uses app default 8001)
  • [x] .env.example — EXISTS. PORT=8001 confirmed, needs update to 8000
Files NOT touched:
  • [x] Core MCP tool logic — boundary clearly stated

Repo Placement

OK. Issue filed on gcal-mcp-remote, all work happens in gcal-mcp-remote. No cross-repo changes needed for this ticket. ArgoCD Application creation in pal-e-deployments is correctly flagged as a Constraint (external dependency), not a file target.

Dependencies

  • Harbor project — must exist before CI push. Documented in Constraints.
  • Woodpecker secrets — harbor_username, harbor_password, forgejo_token. Documented in Constraints.
  • ArgoCD Application — must be created in pal-e-deployments. Documented in Constraints.
  • Stale PR#4 — decision resolved: "Close stale PR#4 and start fresh from main." Documented in Constraints with rationale (mcp-remote-auth migration changed server.py).
  • Board dependencies — board item #65 (linkedin-scheduler-remote, same pattern, done) is a sibling. No blockers found on the board.

Acceptance Criteria

All 3 criteria are testable by an agent:
  • CI pipeline: verifiable via Woodpecker pipeline run after push to main
  • ArgoCD sync + deployment: verifiable via kubectl once ArgoCD Application is wired
  • /metrics endpoint: verifiable via curl
Test Expectations add 6 concrete commands (ruff check, docker build, curl /health, curl /metrics, kubectl dry-run, ruff check as CI gate). All are executable.

Blast Radius

  • Sibling service: linkedin-scheduler-remote (board item #65, done) was the same pattern. No blast radius — these are independent deployments.
  • notion-mcp-remote is the reference implementation (flat layout). The issue correctly calls out the src/ layout difference and how it affects Dockerfile COPY and ENTRYPOINT.
  • Port change 8001 to 8000: affects .env.example and systemd service. No other consumers reference this port externally (MCP services are accessed via Tailscale funnel, not hardcoded ports).

Decomposition

11 file targets (7 create, 4 modify) across 1 repo. 3 AC + 6 test expectations. Borderline on the 5-minute rule by file count alone. However: 6 of 7 "create" files are mechanical boilerplate following the notion-mcp-remote reference pattern (name substitution + port config). The one non-trivial create (Dockerfile.k8s) requires adapting for src/ layout, which is explicitly documented. Single agent pass is appropriate — no decomposition needed.

Recommendation

No action needed. Ticket is ready for next_up.
  • Pre-execution reminder for the agent: Close stale PR#4 before starting work (or at minimum, branch from current main, not from PR#4's branch).