Review: Add Woodpecker CI pipeline and k8s manifests (gcal-mcp-remote) — 3rd pass
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).