Review: Hostname swap step 1 — additive api.pal-e-docs funnel (round 2)

review-972-2026-04-11-r2 Review

review approved

Verdict: APPROVED

Round 2 review of board item #972 (Forgejo forgejo_admin/pal-e-platform#278). Round 1 verdict was NEEDS_REFINEMENT (review-972-2026-04-11). All round 1 findings are resolved or explicitly accepted. Scope is now single-repo, two-new-files, additive-only, and fits the 5-minute rule.

Round 1 Findings — Resolution Check

# Round 1 finding Round 2 status Verified by
1 [FALSE POSITIVE] claim that <code>arch-domain-pal-e-docs</code> does not exist <strong>Refuted — confirmed live</strong> <code>get_note(slug="arch-domain-pal-e-docs")</code> → id=1410, note_type=architecture, project=pal-e-docs, created 2026-04-10, status=active. Round 1 search missed it.
2 [REAL] wrong file targets (<code>terraform/k3s-funnels.tf</code> does not exist) <strong>Resolved</strong> Round 2 pivoted to Option B. Verified precedent <code>~/pal-e-deployments/overlays/pal-e-production/prod/ingress.yaml</code> exists (7-line Ingress using <code>ingressClassName: tailscale</code> + <code>tailscale.com/funnel: "true"</code>). Target files in the new overlay are plausible and do not collide with any existing file.
3 [REAL] decomposition needed (5-minute rule) <strong>Resolved</strong> Scope shrunk to Step 1 only. Single repo (<code>pal-e-deployments</code>), 2 new files, 8 ACs (all curl/kubectl verifiable), no SDK/MCP/frontend changes. Steps 2-7 filed as <code>forgejo_admin/pal-e-platform#280</code> (verified open).
4 [REAL] <code>arch:k8s-deploy</code> has no backing arch note <strong>Accepted as known debt</strong> Ticket explicitly acknowledges the gap, cites sibling tickets #234/#613/#973 also using the label, and defers a proper <code>arch-deployment-pal-e-docs</code> note as out-of-scope. Consistent with the traceability triangle convention's allowance for foundational-work gaps.
5 [REAL] deferred decisions in ticket body <strong>Resolved</strong> All redirect/decommission/swap choices moved to follow-up #280. This ticket has no scoping deferrals — every acceptance criterion is concrete.
6 [REAL] same-namespace ingress backend constraint <strong>Trivially satisfied</strong> New Ingress and existing <code>pal-e-docs</code> Service both live in the <code>pal-e-docs</code> namespace. Verified via <code>~/pal-e-deployments/overlays/pal-e-docs/prod/kustomization.yaml</code> which renames the base Service to <code>pal-e-docs</code>.

Template Completeness

  • [x] Type — Feature
  • [x] Lineage — covers both discovery context and round 1 refutation
  • [x] Repo — forgejo_admin/pal-e-deployments
  • [x] User Story — reader-browse, correctly traced to project-pal-e-docs
  • [x] Architecture — arch:notes + arch:k8s-deploy (latter flagged as debt)
  • [x] Context — current-state table + target-state table, clear before/after
  • [x] Rationale — preserved verbatim from round 1 for decision audit trail
  • [x] File Targets — two new files with precedent path for templating
  • [x] Acceptance Criteria — 8 concrete, verifiable
  • [x] Test Expectations — 4 smoke tests including regression on existing hostname
  • [x] Constraints — reversibility explicit, "do not touch" list complete
  • [x] Checklist — standard
  • [x] Related — dependency, follow-up, arch note, convention, precedent path, round 1 review

Traceability

  • [x] story:reader-browse label — "I can browse public notes, plans, and project pages in a web UI without authentication." Verified in project-pal-e-docs user-stories table (row 5).
  • [x] story note verified — foundation is live at project-pal-e-docs#user-stories
  • [x] arch:notes label — maps to the notes component row in arch-domain-pal-e-docs (id 1410). The new funnel exposes the FastAPI surface that serves note-shaped resources, so the label is load-bearing.
  • [x] arch note verified — arch-domain-pal-e-docs id=1410 exists (refutes round 1 false positive)
  • [!] arch:k8s-deploy label — no backing note. Accepted as known debt per ticket body and per sibling-ticket precedent (#234, #613, #973). Tracked for future arch-deployment-pal-e-docs. Not a blocker for this ticket.
  • [x] Forgejo issue — pal-e-platform#278, state=open

File Targets

  • [x] overlays/pal-e-docs-api/prod/kustomization.yaml — NEW. Parent dir does not exist yet (verified: ~/pal-e-deployments/overlays/ lists 14 overlays, no pal-e-docs-api). Creating a new overlay directory is the correct pattern and matches how pal-e-production, pal-e-streamlit, westside-email, etc. are structured.
  • [x] overlays/pal-e-docs-api/prod/ingress.yaml — NEW. The template reference (overlays/pal-e-production/prod/ingress.yaml) exists and is 16 lines — literally just Ingress with ingressClassName: tailscale, tailscale.com/funnel: "true", and a defaultBackend.service ref. Copy-edit-commit is a <5-minute task.
  • [x] Unmanaged pal-e-docs-funnel — correctly flagged as "do not touch". Verified: ~/pal-e-deployments/overlays/pal-e-docs/prod/ contains kustomization.yaml, deployment-patch.yaml, embedding-worker.yaml, harbor-creds.enc.yamlno ingress.yaml, confirming the existing funnel is not managed by this overlay. Adding a new overlay next to it is safe and non-overlapping.
  • [x] Backend service ref pal-e-docs in namespace pal-e-docs — verified via overlays/pal-e-docs/prod/kustomization.yaml which renames the base Service to pal-e-docs. Port resolves at dev time via kubectl get svc -n pal-e-docs pal-e-docs (base service.yaml uses 8000). Not a scoping gap — one-command lookup.

Repo Placement

Correct. Issue is filed on forgejo_admin/pal-e-platform (the bootstrap repo, matching where the #278/#280/#234/#613 tickets live) but explicitly names forgejo_admin/pal-e-deployments as the implementation repo. The PR will open against pal-e-deployments. This is the standard split in this org: platform holds the meta-tickets, deployments holds the kustomize code.

Dependencies

  • Soft dep on pal-e-api#256 (CORS middleware) — ticket correctly notes that Step 1 does not require CORS (no browser-side fetches change) but flags it as a prerequisite for later steps. Not a blocker for #278.
  • Unblocks follow-up #280 — verified open. Steps 2-7 depend on the additive funnel being live.
  • Board item #972 is not blocked by any other in-flight item in in_progress based on the board listing.

Acceptance Criteria

All 8 ACs are concrete and verifiable by a dev agent without judgment calls. The ArgoCD sync check and the dual-ingress check give clean go/no-go signals. The cert smoke test acknowledges Tailscale cert provisioning latency implicitly (the AC is phrased as "after Tailscale provisions the cert"). The MCP smoke test being explicitly marked as "smoke test only, not a permanent migration" is exactly right — prevents scope creep into Step 3.

Blast Radius

Minimal and well-contained.
  • Additive only: creates one new Ingress object in one namespace, zero modifications to existing resources.
  • Reversible: kubectl delete ingress pal-e-docs-api-funnel -n pal-e-docs (explicitly called out in ticket Constraints).
  • No SDK/MCP/frontend/CORS changes in this PR — all deferred to follow-up.
  • The existing pal-e-docs-funnel remains unmanaged and untouched, so there is zero risk to current MCP, SDK, and CLAUDE.md consumers.
  • Sibling overlay pattern (pal-e-production-funnel) has been in production for 7 days with no issues — precedent is battle-tested.

Decomposition Assessment

No decomposition needed. Against the 5-minute rule:
  • Files: 2 new files in 1 repo (limit: >3 files across >2 repos). Well under.
  • Acceptance criteria: 8 ACs (limit: >5). Slightly over the nominal threshold, BUT all 8 are mechanical verifications of the same single change (kustomize create → ArgoCD sync → kubectl get → curl). They are not independent work units — they are verification facets of one atomic change. Splitting would be artificial.
  • Estimated agent work: Copy precedent ingress.yaml (7 lines), change hostname, change service name, create kustomization.yaml referencing ingress.yaml, commit, PR. Realistic estimate: 3-5 minutes for the agent, plus ArgoCD sync + Tailscale cert provisioning wall-clock (not agent wall-clock).
  • Repos: 1 (pal-e-deployments). Limit: >2. Well under.
Round 2 decomposition is exactly the right grain. Further splitting would be muda.

Round 1 Findings Not Yet Addressed

None. Every round 1 finding is either resolved (#2, #3, #5, #6), refuted with evidence (#1), or explicitly accepted as known debt with a forwarding path (#4).

Minor Observations (non-blocking)

  • [LABEL] Board item #972 title on board-pal-e-docs still reads "Swap hostname routing — pal-e-docs serves frontend, api.pal-e-docs serves API" (the round 1 framing). The Forgejo issue title has been updated to "Hostname swap step 1 — add additive api.pal-e-docs funnel via pal-e-deployments kustomize". Consider updating the board item display title to match — but this is metadata hygiene, not a scope issue, and should not block advancement.
  • Round 1 review note review-972-2026-04-11 is referenced in the ticket body but a search of pal-e-docs returns no note with that slug. Not blocking — the round 1 findings are preserved verbatim in the ticket Lineage section, which is the load-bearing artifact.

Recommendation

APPROVED. Ticket is ready to advance backlog → todo. No blockers.
  • [LABEL] (optional, non-blocking) Update board item #972 display title on board-pal-e-docs to match the current Forgejo issue title.
All other round 1 findings are resolved. Dispatch to dev when Ava is ready.