Westside RoR

westside-ror forgejo

Notes

Review 19
  • Verdict: APPROVED

    Third review pass. All critical issues from R1 (wrong repo, wrong ingress assumptions, wrong secret pattern, port mismatch, overlap with #28) and R2 (wrong repo owner, files said "create" instead of "rewrite") have been resolved. The issue body is now accurate and agent-ready.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- child of #40, depends on #41
    • [x] Repo -- ldraney/pal-e-deployments (correct)
    • [x] User Story -- present and clear
    • [x] Context -- thorough explanation of why existing files must be rewritten, references PR #189/#28, describes platform pattern
    • [x] File Targets -- present with correct rewrite/create distinction
    • [x] Acceptance Criteria -- 10 criteria, all testable
    • [x] Test Expectations -- includes kustomize build dry-run command
    • [x] Constraints -- clear on target repo, patterns, SOPS, port
    • [x] Checklist -- present
    • [x] Related -- comprehensive cross-references

    Traceability

    • [x] story:ci-pipeline label -- present on board item
    • [ ] story note NOT verified -- project-westside-ror note does not exist in pal-e-docs. [SCOPE] Create project page with user-stories section (acknowledged non-blocking in R1/R2)
    • [x] arch:k8s label -- present on board item
    • [ ] arch note NOT verified -- no arch-k8s note found in pal-e-docs. [SCOPE] Create architecture note arch-k8s (acknowledged non-blocking in R1/R2)
    • [x] Forgejo issue -- ldraney/westside-ror#42, open

    File Targets

    • [x] overlays/westside-ror/prod/kustomization.yaml -- verified EXISTS in pal-e-deployments. Currently references bases/standard, uses ruby:3.4-slim image, removes imagePullSecrets. Correctly marked as "rewrite"
    • [x] overlays/westside-ror/prod/deployment-patch.yaml -- verified EXISTS. Currently has hostPath mount, inline apt-get/bundle, envFrom secretRef. Correctly marked as "rewrite"
    • [x] overlays/westside-ror/prod/ingress.yaml -- verified DOES NOT EXIST in prod (exists in dev). Correctly marked as "create"
    • [x] overlays/westside-ror/prod/ingress-westsidekingsandqueens.yaml -- verified DOES NOT EXIST. Correctly marked as "create"
    • [x] overlays/westside-ror/prod/secrets.enc.yaml -- verified DOES NOT EXIST. Correctly marked as "create". SOPS pattern verified via believers-elite/prod/secrets.enc.yaml
    • [x] overlays/westside-ror/prod/harbor-creds.enc.yaml -- verified DOES NOT EXIST. Correctly marked as "create". SOPS pattern verified via westsidekingsandqueens/prod/harbor-creds.enc.yaml

    Reference patterns verified:

    • landscaping-assistant/prod/ -- confirmed kustomization.yaml sets namespace, references bases/standard, uses Harbor image tag, deployment-patch has initContainer, individual secretKeyRef entries, security context, probes. Only has 2 files (no secrets.enc.yaml -- secrets likely managed separately)
    • westsidekingsandqueens/prod/ -- confirmed has harbor-creds.enc.yaml and ingress.yaml with Tailscale Funnel pattern
    • believers-elite/prod/ -- confirmed has secrets.enc.yaml with SOPS age encryption
    • .sops.yaml -- confirmed path_regex \.enc\.yaml$ with age recipient key

    Repo Placement

    Correct. Issue is tracked on ldraney/westside-ror for board visibility, but clearly states PR must target ldraney/pal-e-deployments. This is documented in the Repo section, Constraints, and Checklist. All three locations now say ldraney/pal-e-deployments (R2 fix confirmed).

    Dependencies

    • #41 (Terraform onboarding) -- prerequisite, creates namespace + ArgoCD app. Status: OPEN. Documented in Lineage and Related. This ticket cannot be fully validated (ArgoCD sync) until #41 ships, but the manifests themselves can be written independently.
    • #43 (Dockerfile + CI pipeline) -- sibling, sets port 3000 in Dockerfile. Status: OPEN. Documented in Context and Related. The manifests reference port 3000 which aligns with #43's plan to drop Thruster.
    • #28 (Commit prod deployment manifests) -- predecessor, DONE. Created the existing dev-like prod files that this ticket replaces. Documented in Context and Related.
    • #40 (Epic: CI/CD pipeline) -- parent epic. In BACKLOG. Documented in Lineage.

    Acceptance Criteria

    10 criteria, all verifiable by an agent:

    • AC 1-4: Structural checks -- agent can verify via grep/read after writing files
    • AC 5: Security context -- agent can compare against landscaping-assistant pattern
    • AC 6: Ingress presence -- agent can verify files exist
    • AC 7-8: SOPS secrets -- agent can create with sops --encrypt and verify key count
    • AC 9: Port 3000 -- agent can grep rendered output
    • AC 10: kustomize build -- agent can run the test command

    Test command is real: kustomize build overlays/westside-ror/prod/ | kubectl apply --dry-run=client -f -

    Blast Radius

    • westsidekingsandqueens ingress conflict risk: The issue says to create ingress-westsidekingsandqueens.yaml in the westside-ror namespace for the westsidekingsandqueens hostname. The existing westsidekingsandqueens/prod/ingress.yaml currently serves that hostname from the westsidekingsandqueens namespace. The README says westsidekingsandqueens is "being replaced by westside-ror" but is "still running as the active production frontend." The issue's Context section explains this: the westsidekingsandqueens hostname was "manually created in westside-ror namespace after consolidation." This is consistent with the consolidation story. The agent implementing this should be aware that the old overlay's ingress may need to be removed separately, but the issue correctly scopes the DO-NOT-TOUCH list to exclude that.
    • No impact on dev overlay -- correctly excluded from file targets
    • No impact on bases/standard -- correctly excluded, patches only
    • DATABASE_URL construction: The existing deployment-patch constructs DATABASE_URL from individual env vars. The new pattern uses individual secretKeyRef entries. The issue lists the correct 6 keys but does not mention DATABASE_URL construction. The agent will need to add a DATABASE_URL env var that interpolates the secret values, matching the current pattern. This is implicit in "follow landscaping-assistant pattern" but worth noting.

    Decomposition Assessment

    6 file targets across 1 repo (pal-e-deployments). 10 acceptance criteria (above the 5-criterion threshold). However:

    • All files are in the same directory (overlays/westside-ror/prod/)
    • Strong reference patterns exist (landscaping-assistant, westsidekingsandqueens, believers-elite)
    • 2 files are rewrites of existing content, 4 are new but follow established patterns
    • SOPS encryption is mechanical (sops --encrypt with age key)
    • Estimated agent time: ~5 minutes -- borderline but feasible in a single pass given the pattern-following nature

    No decomposition needed. The high AC count reflects thoroughness of verification, not complexity of implementation.

    Observations (non-blocking)

    • Missing namespace: in kustomization.yaml: The existing westside-ror kustomization does NOT set namespace: westside-ror, but the landscaping-assistant and westsidekingsandqueens patterns both set their namespace explicitly. The issue says "follow landscaping-assistant pattern" which implies the agent should add it. Not a ticket defect -- the agent should follow the pattern.
    • Missing SOPS secrets.enc.yaml reference in kustomization.yaml: The issue's kustomization.yaml rewrite should add secrets.enc.yaml and harbor-creds.enc.yaml to the resources: list (as westsidekingsandqueens does for harbor-creds). Not explicitly stated but implied by "follow pattern."
    • ServiceMonitor rename: The landscaping-assistant pattern renames the ServiceMonitor. The existing westside-ror kustomization does not. The agent should follow the pattern and add it.
    • Project page and arch note: project-westside-ror and arch-k8s do not exist in pal-e-docs. Acknowledged as non-blocking in previous reviews.

    Recommendation

    • [SCOPE] Create project-westside-ror project page with user-stories section in pal-e-docs (non-blocking, acknowledged)
    • [SCOPE] Create arch-k8s architecture note in pal-e-docs (non-blocking, acknowledged)

    No blocking issues remain. Ticket is agent-ready.

  • Verdict: APPROVED

    Re-review of board item #1374. Previous review review-1374-2026-06-06 returned NEEDS_REFINEMENT with 4 findings. Two [BODY] fixes confirmed applied; two [SCOPE] gaps acknowledged as known and non-blocking.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- child of #40
    • [x] Repo -- forgejo_admin/pal-e-services (explicit NOTE clarifies cross-repo tracking)
    • [x] User Story
    • [x] Context
    • [x] File Targets
    • [x] Acceptance Criteria
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    All template sections present and complete.

    Traceability

    • [x] story:ci-pipeline label -- present on board item
    • [ ] story note MISSING -- project-westside-ror note does not exist. Known gap, not blocking (issue body is self-contained). [SCOPE] Create project page with user-stories section.
    • [x] arch:terraform label -- present on board item
    • [ ] arch note MISSING -- No arch-terraform note in pal-e-docs. Known gap, not blocking. [SCOPE] Create architecture note arch-terraform.
    • [x] Forgejo issue -- ldraney/westside-ror#41, state: open

    File Targets

    • [x] pal-e-services/terraform/k3s.tfvars -- verified exists (225 lines). Services block spans lines 137-225. 10 existing service entries (issue says 9, minor). westside-ror NOT yet present -- correct.
    • [x] Line reference FIXED -- now reads "services block spans approximately lines 137-225; individual entries are ~8 lines each". Verified accurate against file.

    Repo Placement

    FIXED. Cross-repo instruction is now explicit and repeated in three locations:

    • Repo section: NOTE block explains issue is tracked on westside-ror for board visibility but PR targets forgejo_admin/pal-e-services
    • Constraints: "PR must be opened against forgejo_admin/pal-e-services, NOT ldraney/westside-ror"
    • Checklist: "PR opened (on forgejo_admin/pal-e-services)"

    An implementing agent will not miss this.

    Dependencies

    • #40 (board #1372) -- parent epic, backlog. Not blocking.
    • #42 (board #1375) -- K8s manifests, backlog. Depends on this ticket (needs Harbor project). Documented.
    • #43 (board #1376) -- Dockerfile + CI, backlog. Depends on this (needs Harbor creds). Documented.
    • No in_progress or next_up items block this ticket.

    Acceptance Criteria

    6 acceptance criteria. AC 1-2 are code-verifiable (grep the entry, match pattern). AC 3-6 are infrastructure validation steps requiring terraform CLI + live cluster access -- manual post-merge steps. The code change itself is a single file edit. Acceptable scope.

    Blast Radius

    • Existing namespace resources (rails-env secret, Tailscale Funnel ingresses) are documented as a risk. Constraints section addresses this explicitly.
    • ArgoCD pruning risk documented in Context. #42 will handle including these in managed manifests.
    • No similar pattern bugs across sibling services -- well-established block with 10 entries.

    Decomposition Assessment

    1 file target, 1 repo (pal-e-services), 6 AC (1 code change + 5 infra validation). Estimated agent work under 5 minutes. No decomposition needed.

    Previous Findings Resolution

    • [x] [BODY] Line reference -- FIXED. Now accurately describes services block span.
    • [x] [BODY] Cross-repo PR instruction -- FIXED. Explicit NOTE added, repeated in Constraints and Checklist.
    • [ ] [SCOPE] No project page -- still missing. Known gap, non-blocking.
    • [ ] [SCOPE] No arch-terraform note -- still missing. Known gap, non-blocking.

    Recommendation

    No action needed. Ticket is ready for implementation.

    Remaining [SCOPE] items for future backlog:

    • [SCOPE] Create project page project-westside-ror with user-stories section including ci-pipeline story.
    • [SCOPE] Create architecture note arch-terraform documenting the terraform services module pattern.
  • Verdict: APPROVED

    Re-review of board item #1376 after refinement. Previous review: review-1376-2026-06-06 (NEEDS_REFINEMENT).

    Previous Findings — Resolution

    • [x] Test framework mismatch (RSpec vs Minitest) — FIXED. Issue Context now states: "westside-ror uses Minitest (bundle exec rails test)". AC4 explicitly requires bundle exec rails test, NOT bundle exec rspec. Constraints section reiterates: "Use Minitest (bundle exec rails test), NOT RSpec — westside-ror has test/ dir, no spec/ dir, no rspec in Gemfile." Test Expectations run command also uses bundle exec rails test.
    • [x] AC7/AC8 unachievable in PR — FIXED. AC1-AC7 are now in-PR criteria. AC8-AC9 are in a separate "Post-merge manual steps" section with note: "depend on #41 completing first." Clear separation.
    • [ ] Project page missing — Known gap, non-blocking. project-westside-ror page does not exist in pal-e-docs. [SCOPE] remains.
    • [ ] Arch note missing — Known gap, non-blocking. arch-ci-cd note does not exist in pal-e-docs. [SCOPE] remains.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — child of #40, depends on #41
    • [x] Repo — ldraney/westside-ror
    • [x] User Story
    • [x] Context — includes reference implementation and explicit Minitest callout
    • [x] File Targets — modify (Dockerfile, bin/docker-entrypoint), create (.woodpecker.yaml), do-not-touch (k8s/dev.yaml, k8s/prod/*)
    • [x] Acceptance Criteria — 7 in-PR criteria, 2 post-merge manual steps
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    Traceability

    • [x] story:ci-pipeline label present
    • [ ] story note MISSING — [SCOPE] Project page project-westside-ror does not exist. Known gap, non-blocking.
    • [x] arch:ci-cd label present
    • [ ] arch note MISSING — [SCOPE] No arch-ci-cd note exists. Known gap, non-blocking.
    • [x] Forgejo issue — #43, open

    File Targets

    • [x] Dockerfile — verified: exists at repo root, uses docker.io/library/ruby:3.4.8-slim. CMD references ./bin/thrust but thrust binary does not exist and thruster gem is not in Gemfile/Gemfile.lock. Issue correctly identifies all three problems (base image, Thruster, port 80).
    • [x] bin/docker-entrypoint — verified: exists, runs db:prepare on server start.
    • [x] .woodpecker.yaml — confirmed does not exist yet (to be created).
    • [x] Reference: ~/landscaping-assistant/.woodpecker.yaml — verified: exists with clone, bundle-install, lint, test, build-and-push steps. Test step uses bundle exec rspec (correctly identified as needing change for westside-ror).
    • [x] test/ directory exists with controllers/, models/, services/, test_helper.rb. No spec/ directory. No rspec in Gemfile. Minitest confirmed.
    • [x] Rubocop available: rubocop-rails-omakase gem in Gemfile, .rubocop.yml exists. Lint step will work.
    • [x] Puma gem present in Gemfile. Port 3000 switch is correct.

    Repo Placement

    OK — issue filed on ldraney/westside-ror, all file targets in same repo. Single-repo scope.

    Dependencies

    • #41 (Terraform service onboarding) — open, board item #1374 in backlog. Creates Harbor project + robot account. Prerequisite for build-and-push to succeed, but correctly documented as post-merge manual step dependency. Does NOT block the PR itself (Dockerfile and .woodpecker.yaml can be written and merged independently).
    • #40 (Epic: CI/CD pipeline) — open, board item #1372 in backlog. Parent epic.
    • #42 (K8s prod manifests) — open, board item #1375 in backlog. Sibling, references images this pipeline pushes. No blocking relationship.

    Acceptance Criteria

    • [x] AC1 (build image base) — agent-verifiable: grep Dockerfile for harbor build image.
    • [x] AC2 (runtime image base) — agent-verifiable: grep Dockerfile for harbor runtime image.
    • [x] AC3 (woodpecker steps) — agent-verifiable: parse .woodpecker.yaml for step names.
    • [x] AC4 (Minitest not RSpec) — agent-verifiable: grep .woodpecker.yaml for test command. Explicitly stated.
    • [x] AC5 (Kaniko repo/tag) — agent-verifiable: check build-and-push settings.
    • [x] AC6 (push only on main) — agent-verifiable: check when clause.
    • [x] AC7 (lint+test on push+PR) — agent-verifiable: check when clauses on lint/test steps.
    • [x] Post-merge AC8-AC9 — correctly separated, depend on #41. Not in PR scope.

    Blast Radius

    • Dockerfile change is self-contained to westside-ror.
    • Thruster reference removal is safe — binary and gem do not exist in repo.
    • No other repos affected.

    Decomposition Assessment

    2 file modifications + 1 file creation, single repo, 7 in-PR acceptance criteria. Core agent work is well under the 5-minute rule. No decomposition needed.

    Recommendations

    No action needed. All previous [BODY] recommendations have been addressed. Remaining [SCOPE] items (project page, arch note) are known gaps and non-blocking for this ticket.

    • [SCOPE] Create project page project-westside-ror with user-stories section (non-blocking, pre-existing gap).
    • [SCOPE] Create architecture note arch-ci-cd (non-blocking, pre-existing gap).
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — child of #40, depends on #41
    • [x] Repo — ldraney/westside-ror
    • [x] User Story
    • [x] Context
    • [x] File Targets
    • [x] Acceptance Criteria
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    All required feature template sections present.

    Traceability

    • [x] story:ci-pipeline label — present on board item
    • [ ] story note MISSING — project-westside-ror note does not exist in pal-e-docs (404). [SCOPE] Create project page project-westside-ror with user-stories section including ci-pipeline.
    • [x] arch:k8s label — present on board item
    • [ ] arch note MISSING — search for arch-k8s returned zero results. [SCOPE] Create architecture note arch-k8s for the k8s manifest component.
    • [x] Forgejo issue — ldraney/westside-ror#42, state: open

    File Targets

    • [ ] k8s/prod/deployment.yaml — ISSUE: Wrong repo. The platform pattern places production manifests in pal-e-deployments/overlays/westside-ror/prod/, not in the app repo. A westside-ror/overlays/westside-ror/dev/ overlay already exists in pal-e-deployments. The sibling service westsidekingsandqueens has its prod overlay at pal-e-deployments/overlays/westsidekingsandqueens/prod/. Some older services (pal-e-docs, basketball-api) still have in-repo k8s/ dirs, but the platform has converged on pal-e-deployments.
    • [ ] k8s/prod/service.yaml — ISSUE: Same — belongs in pal-e-deployments.
    • [ ] k8s/prod/ingress-westside-ror.yaml — ISSUE: Same repo problem. Additionally, file already exists functionally in k8s/dev.yaml (lines 131-148) as a combined manifest.
    • [ ] k8s/prod/ingress-westsidekingsandqueens.yaml — ISSUE: Wrong assumption. The westsidekingsandqueens Tailscale Funnel ingress belongs to the westside-app (westsidekingsandqueens) deployment, NOT to westside-ror. It already exists at pal-e-deployments/overlays/westsidekingsandqueens/prod/ingress.yaml pointing to the westside-app service. Creating a second ingress for the same hostname would conflict.
    • [ ] k8s/prod/kustomization.yaml — ISSUE: The platform pattern uses pal-e-deployments with bases/standard as a kustomize base (deployment, service, networkpolicy). Prod overlays reference ../../../bases/standard and apply patches. This file should be in pal-e-deployments.
    • [ ] k8s/prod/sealed-secret.yaml — ISSUE: Platform uses SOPS (age encryption), not sealed-secrets. See pal-e-deployments/overlays/westsidekingsandqueens/prod/harbor-creds.enc.yaml and westside-app/k8s/auth-secret.enc.yaml for the pattern. File should be rails-env.enc.yaml in the overlay.
    • [x] k8s/dev.yaml — verified exists, ticket correctly says not to touch it.

    Repo Placement

    MAJOR MISMATCH. The issue says repo is ldraney/westside-ror and all file targets are k8s/prod/* inside the app repo. However, the established platform pattern places production manifests in pal-e-deployments. The closed issue #28 ("Commit prod deployment manifests") was correctly filed against ldraney/pal-e-deployments. This ticket should either:

    1. Target pal-e-deployments/overlays/westside-ror/prod/ as the file location (matching the pattern from westsidekingsandqueens/prod/), OR
    2. Be filed on the pal-e-deployments repo if the team wants the Forgejo issue to live with the code it modifies.

    Dependencies

    • #41 (Terraform onboarding) — documented as prerequisite, state: open. Creates namespace, Harbor project, and ArgoCD app via terraform. This ticket correctly depends on it.
    • #43 (Dockerfile + Woodpecker CI) — documented as related, state: open. Pushes images to Harbor that these manifests reference. The deployment manifest needs a valid Harbor image to pull.
    • #28 (Commit prod deployment manifests) — state: closed/done. This was the PREVIOUS attempt to commit prod manifests, filed against pal-e-deployments. Overlap with this ticket needs clarification — was #28 completed, partially done, or superseded?
    • #1358 board item — board item for #28 is in done column. If that work was completed, this ticket may be partially or fully redundant.

    Acceptance Criteria

    • [x] k8s/prod/ directory contains all manifests — testable but wrong directory per platform pattern
    • [x] Deployment references Harbor image — testable, clear
    • [ ] Both Tailscale Funnel ingresses present — ISSUE: westsidekingsandqueens ingress belongs to the separate westside-app service, not westside-ror. Only westside-ror funnel should be created here.
    • [x] Secret manifest includes all env vars — testable, clear list provided
    • [ ] Secret management follows platform pattern — ticket says "SOPS, sealed-secrets, or external-secrets — match landscaping-assistant" but landscaping-assistant has NO k8s directory. The reference should be westsidekingsandqueens overlay in pal-e-deployments, which uses SOPS with age encryption.
    • [x] docker-entrypoint runs db:prepare — verified: bin/docker-entrypoint runs db:prepare when starting rails server
    • [x] Dev overlay untouched — clear, testable

    Blast Radius

    • Ingress hostname conflict: Creating a westsidekingsandqueens ingress under westside-ror would conflict with the existing ingress in pal-e-deployments/overlays/westsidekingsandqueens/prod/ingress.yaml. Tailscale Funnel hostnames are unique per cluster.
    • Overlap with closed #28: If issue #28 already committed manifests to pal-e-deployments, creating a second set in the app repo creates drift and confusion about source of truth.
    • Dockerfile entrypoint port: The Dockerfile uses Thruster and EXPOSEs port 80 (not 3000). The deployment and service manifests need to match — either target port 80 or override the CMD.

    Decomposition Assessment

    6 file targets (would be ~4 after corrections), 7 acceptance criteria. However, all files are in a single directory and the work is straightforward manifest creation. With corrected scope (single repo, ~4 files), this fits in a single agent pass. No decomposition needed after scope fixes are applied.

    Recommendation

    • [BODY] Fix repo: change from ldraney/westside-ror to ldraney/pal-e-deployments, or clarify that file targets are in pal-e-deployments/overlays/westside-ror/prod/.
    • [BODY] Fix file targets: rewrite all paths from k8s/prod/* to overlays/westside-ror/prod/* in pal-e-deployments.
    • [BODY] Remove ingress-westsidekingsandqueens.yaml target — that ingress belongs to the separate westsidekingsandqueens overlay and already exists.
    • [BODY] Fix secret pattern reference: change "match landscaping-assistant" to "match westsidekingsandqueens overlay in pal-e-deployments (SOPS with age encryption)". Use rails-env.enc.yaml naming.
    • [BODY] Add note about kustomize base: prod overlay should reference ../../../bases/standard and apply patches (matching westsidekingsandqueens/prod/kustomization.yaml pattern).
    • [BODY] Fix AC #3: remove "westsidekingsandqueens" from the Funnel ingress criterion — only westside-ror funnel is needed.
    • [BODY] Fix AC #5: specify SOPS with age, not "SOPS, sealed-secrets, or external-secrets".
    • [BODY] Add AC: verify container port matches Dockerfile (port 80 via Thruster, not 3000).
    • [BODY] Clarify relationship with closed #28 — is this a replacement, continuation, or does #28 need reopening?
    • [SCOPE] Create project page project-westside-ror with user-stories section including ci-pipeline story.
    • [SCOPE] Create architecture note arch-k8s for the k8s manifest component.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type
    • [x] Lineage
    • [x] Repo
    • [x] User Story
    • [x] Context
    • [x] File Targets
    • [x] Acceptance Criteria
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    All template sections present. Template completeness is good.

    Traceability

    • [x] story:ci-pipeline label -- present on board item
    • [ ] story note MISSING -- [SCOPE] project-westside-ror note does not exist; cannot verify user story entry. Create project page with user-stories section.
    • [x] arch:terraform label -- present on board item
    • [ ] arch note MISSING -- [SCOPE] No arch-terraform note found in pal-e-docs. Create architecture note arch-terraform for the terraform services module.
    • [x] Forgejo issue -- ldraney/westside-ror#41, state: open

    File Targets

    • [x] pal-e-services/terraform/k3s.tfvars -- verified exists (225 lines). Services block starts at line 137. westside-ror is NOT yet present in the file.
    • [ ] Line reference inaccurate -- [BODY] Issue says "Reference: landscaping-assistant entry in k3s.tfvars (lines 137-225)". The landscaping-assistant service entry is actually at lines 147-154; lines 137-225 is the entire services block. Recommend clarifying to "lines 147-154" for the landscaping-assistant entry pattern.

    Repo Placement

    MISMATCH: The issue body declares ### Repo: forgejo_admin/pal-e-services (the repo where the file change lives), but the Forgejo issue is filed on ldraney/westside-ror. This is a cross-repo issue -- the actual code change is in pal-e-services, not westside-ror. The implementing agent needs to clone/checkout pal-e-services, not westside-ror.

    [BODY] Clarify that the PR should be opened against forgejo_admin/pal-e-services, not ldraney/westside-ror. Consider whether the Forgejo issue should be filed on pal-e-services instead, or add explicit instructions that the agent must work in the pal-e-services repo.

    Dependencies

    • #40 (board item #1372) -- parent epic "CI/CD pipeline", backlog. This ticket is a child.
    • #42 (board item #1375) -- "K8s prod manifests + secrets for ArgoCD deployment", backlog. Depends on this ticket (needs Harbor project for image refs). Documented in issue body.
    • #43 (board item #1376) -- "Dockerfile + Woodpecker CI pipeline", backlog. Depends on this indirectly (CI needs Harbor to push images).
    • No items in in_progress or next_up block this ticket.
    • The k8s/prod directory referenced in the ArgoCD source_path does NOT exist yet in westside-ror -- expected, as #42 will create it. ArgoCD app will be unhealthy until then (documented in AC).

    Acceptance Criteria

    5 acceptance criteria -- at the upper bound but acceptable for a single agent pass.

    • AC 1 (add services entry) -- verifiable by grep/read
    • AC 2 (terraform plan shows resources) -- verifiable but requires terraform CLI access and credentials
    • AC 3 (terraform apply succeeds) -- requires live infra access, not automatable by code agent alone
    • AC 4 (Harbor project exists) -- requires Harbor API/UI access post-apply
    • AC 5 (ArgoCD app created) -- requires ArgoCD access post-apply

    AC 2-5 are infrastructure verification steps that happen after the code change. The code change itself (AC 1) is straightforward. An agent can handle the code change + PR; AC 2-5 are manual validation steps.

    Blast Radius

    • Existing namespace resources: rails-env secret and Tailscale Funnel ingresses are manually created in the westside-ror namespace. Terraform namespace resource could conflict. Constraints section documents this risk correctly.
    • No similar pattern bugs found -- the services block is well-established with 9 existing entries.
    • ArgoCD pruning policy is a risk factor -- if ArgoCD prunes unmanaged resources in the namespace, the manually-created resources could be deleted. This is noted in the Context section.

    Decomposition Assessment

    1 file target, 1 repo (pal-e-services), 5 AC (but only 1 is a code change). Estimated agent work: under 5 minutes for the code change + PR. No decomposition needed.

    Recommendation

    • [BODY] Fix line reference: "landscaping-assistant entry in k3s.tfvars (lines 137-225)" should be "(lines 147-154)" for the service entry pattern, or "(services block: lines 137-225)" for the full block.
    • [BODY] Add explicit instruction that the PR must be opened against forgejo_admin/pal-e-services, and the agent must clone/work in that repo, not westside-ror.
    • [SCOPE] Create project page project-westside-ror with a user-stories section that includes the ci-pipeline story.
    • [SCOPE] Create architecture note arch-terraform documenting the terraform services module pattern.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- child of #40, depends on #41
    • [x] Repo -- ldraney/westside-ror
    • [x] User Story
    • [x] Context
    • [x] File Targets
    • [x] Acceptance Criteria
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    Traceability

    • [x] story:ci-pipeline label present
    • [ ] story note MISSING -- [SCOPE] Project page project-westside-ror does not exist in pal-e-docs. Cannot verify user story entry. Create project page with user-stories section.
    • [x] arch:ci-cd label present
    • [ ] arch note MISSING -- [SCOPE] No arch-ci-cd note found in pal-e-docs. Create architecture note for CI/CD component.
    • [x] Forgejo issue -- #43, open

    File Targets

    • [x] Dockerfile -- verified: exists, uses docker.io/library/ruby:3.4.8-slim as claimed. Thruster reference in CMD but bin/thrust does NOT exist and thruster gem is NOT in Gemfile/Gemfile.lock. Issue correctly identifies this needs changing.
    • [x] bin/docker-entrypoint -- verified: exists, db:prepare runs correctly.
    • [x] .woodpecker.yaml -- confirmed does not exist yet (to be created).
    • [x] Reference: ~/landscaping-assistant/.woodpecker.yaml -- verified exists, structure matches what issue describes (clone, bundle-install, lint, test, build-and-push steps).
    • [x] Reference: ~/landscaping-assistant/Dockerfile -- verified uses ruby-rails-build:latest / ruby-rails-runtime:latest, exposes port 3000, uses Puma directly.

    Repo Placement

    OK -- issue is filed on ldraney/westside-ror, all file targets are in that repo. Single-repo scope.

    Dependencies

    • #41 (Terraform service onboarding) -- open, creates Harbor project + robot account. This is a prerequisite: without the Harbor project, Kaniko build-and-push will fail. Documented in issue Lineage section. Board item #1374 is in backlog.
    • #40 (Epic: CI/CD pipeline) -- open, parent epic. Board item #1372 in backlog.
    • #42 (K8s prod manifests) -- sibling, references images this pipeline pushes. Board item #1375 in backlog. No blocking relationship for this ticket.

    Acceptance Criteria

    • [x] AC1-AC6 are clear and agent-verifiable (file content checks).
    • [ ] AC3 says "test" step should use the reference pattern, but the reference uses bundle exec rspec while westside-ror uses Minitest (test/ directory, no rspec gem). The issue body and AC do not mention this difference. The agent will copy bundle exec rspec from the reference and the pipeline will fail. [BODY] The test step must use bundle exec rails test instead of bundle exec rspec.
    • [ ] AC7 ("Repo activated in Woodpecker") is a manual/API step outside the PR scope. Should be split out or marked as a manual post-merge step.
    • [ ] AC8 ("First pipeline run succeeds") depends on AC7 and on #41 completing first. Not achievable in the same PR.

    Blast Radius

    • Dockerfile change is self-contained to westside-ror.
    • Thruster (bin/thrust) is referenced in the current Dockerfile CMD but does not exist in the repo and the gem is not bundled -- the current Dockerfile is already broken for production. No downstream risk from removing the Thruster reference.
    • No other repos are affected by this change.

    Decomposition Assessment

    2 file modifications + 1 file creation, single repo. 8 acceptance criteria but 2 (AC7, AC8) are manual/post-merge. Core agent work is ~3 files, well under 5-minute rule. No decomposition needed.

    Recommendations

    • [BODY] Add explicit note in Context or Constraints: westside-ror uses Minitest (bundle exec rails test), NOT RSpec. The test step in .woodpecker.yaml must differ from the landscaping-assistant reference.
    • [BODY] Clarify AC3: the test step command should be bundle exec rails test, not bundle exec rspec.
    • [BODY] Clarify AC7-AC8: mark as manual post-merge steps or split into a separate task, since they cannot be verified in a PR.
    • [SCOPE] Create project page project-westside-ror in pal-e-docs with user-stories section including ci-pipeline story.
    • [SCOPE] Create architecture note arch-ci-cd in pal-e-docs for the CI/CD component.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, discovered from undeployed Stripe PRs
    • [x] Repo -- ldraney/westside-ror
    • [x] User Story -- present and well-formed
    • [x] Context -- thorough, references landscaping-assistant pattern
    • [x] File Targets -- present with both modify/create and do-not-touch sections
    • [x] Acceptance Criteria -- 10 items, clear and specific
    • [x] Test Expectations -- present with run commands
    • [x] Constraints -- present with platform patterns, base images, access requirements
    • [x] Checklist -- present
    • [x] Related -- present

    All required sections for the Feature template are present.

    Traceability

    • [x] story:ci-pipeline label -- present on board item
    • [ ] story note MISSING -- [SCOPE] No project page project-westside-ror exists in pal-e-docs, so user-stories section cannot be verified. Create project page with user story entry for ci-pipeline.
    • [x] arch:ci-cd label -- present on board item
    • [ ] arch note MISSING -- [SCOPE] No architecture note arch-ci-cd found in pal-e-docs. Create architecture note arch-ci-cd for CI/CD pipeline component.
    • [x] Forgejo issue -- ldraney/westside-ror#40, open

    File Targets

    • [x] .woodpecker.yaml -- confirmed does NOT exist yet (new file, correct)
    • [x] Dockerfile -- exists, currently uses docker.io/library/ruby:3.4-slim. Needs update to platform base images (ruby-rails-build, ruby-rails-runtime). Verified reference impl at landscaping-assistant/Dockerfile uses harbor.tail5b443a.ts.net/library/ruby-rails-build:latest.
    • [x] k8s/ -- exists with only dev.yaml. Currently references ruby:3.4-slim image with hostPath mount. Ticket correctly identifies need for proper prod manifests referencing Harbor image.
    • [x] bin/docker-entrypoint -- exists and already runs db:prepare on startup. No changes needed.
    • [x] k8s/dev.yaml -- correctly listed under "do not touch"
    • [x] pal-e-services/terraform/k3s.tfvars -- verified: westside-ror is NOT in the services block (line 137-225). Needs to be added. Confirmed other services follow the pattern: forgejo_repo, image_repo, port, funnel, source_repo, source_path.

    Repo Placement

    This ticket correctly identifies work across TWO repos:

    • ldraney/westside-ror -- .woodpecker.yaml, Dockerfile, k8s manifests (Forgejo issue is filed here)
    • ldraney/pal-e-services -- terraform/k3s.tfvars services block

    The issue body explicitly documents the cross-repo file target. However, there is no separate Forgejo issue on pal-e-services for the terraform change. This is acceptable since the terraform change is a single-line addition and is clearly documented in the file targets. A separate issue would be over-engineering.

    Dependencies

    • No board items currently blocking this ticket. Item #1372 is in backlog.
    • Items #1207 (issue #2, in_progress) and #1222 (issue #13, in_progress) are unrelated feature work.
    • Done items #1334 (issue #24) and #1357 (issue #27) are the Stripe work this ticket aims to deploy -- they are completed, not blocking.
    • Done item #1358 (issue #28, "Commit prod deployment manifests") is DIRECTLY related -- it committed deployment manifests but those are the dev overlay. This ticket replaces those with proper Harbor-image manifests. No conflict.
    • The ticket requires platform base images (ruby-rails-build, ruby-rails-runtime) to exist in Harbor. The constraints section mentions this but does not verify it. The landscaping-assistant reference already uses these images successfully.
    • Requires Harbor admin access, Woodpecker admin access, and terraform access to pal-e-services. Documented in Constraints.
    • The westsidekingsandqueens-funnel ingress currently exists in the cluster (verified via kubectl) -- created manually. AC #8 requires preserving both funnel ingresses. The terraform services block will create a new westside-ror funnel, but the westsidekingsandqueens funnel needs special handling since it is a second hostname for the same service. This is not addressed in the file targets.

    Acceptance Criteria

    10 acceptance criteria present. Assessment:

    • AC 1-4 (Harbor project, .woodpecker.yaml, Dockerfile, Woodpecker activation) -- agent-verifiable via API/file checks
    • AC 5-6 (terraform services block, ArgoCD app) -- agent-verifiable via file diff and ArgoCD API
    • AC 7 (k8s manifests reference Harbor image) -- agent-verifiable via file content check
    • AC 8 (both funnel ingresses preserved) -- [BODY] This is the most complex criterion. The westsidekingsandqueens funnel was created via kubectl apply (see docs/consolidation.md). When terraform creates the ArgoCD app, the deployment manifests need to include this second ingress OR it will be lost. The file targets do not mention creating a second ingress manifest. Recommend adding explicit file target for the westsidekingsandqueens funnel ingress in k8s prod manifests.
    • AC 9 (docker-entrypoint runs db:prepare) -- already satisfied, verified in code
    • AC 10 (first pipeline builds and deploys) -- manual verification needed post-implementation

    Blast Radius

    • The westsidekingsandqueens-funnel ingress was manually created (kubectl apply) and is NOT managed by ArgoCD or terraform. When this ticket transitions westside-ror to ArgoCD management, the ArgoCD app will only sync what is in the deployment manifests. If the funnel ingress manifest is not included, the westsidekingsandqueens URL will be lost. This is the highest-risk item.
    • The current dev overlay (k8s/dev.yaml) contains hardcoded secrets (POSTGRES_PASSWORD, SECRET_KEY_BASE). These should NOT be committed to prod manifests. The ticket correctly says to keep dev.yaml as-is.
    • The external CNPG database connection (pal-e-postgres-rw.postgres.svc:5432, database: basketball) must be preserved in the prod manifests. The current dev.yaml references this via env vars -- prod manifests will need equivalent secret references.
    • No other services are affected -- this is a new pipeline for an existing service.

    Decomposition Assessment

    5-minute rule assessment:

    • File targets: 4 files in westside-ror + 1 in pal-e-services = 5 files across 2 repos
    • Acceptance criteria: 10 items
    • Estimated agent work: >5 minutes (Harbor project creation, Woodpecker activation, terraform apply, ArgoCD sync, pipeline validation)
    • However, several AC items require manual admin operations (Harbor project creation, Woodpecker repo activation, terraform apply) that cannot be done by a code agent alone.

    NEEDS DECOMPOSITION -- 10 AC across 2 repos with manual admin steps. However, the decomposition is natural: (1) code changes PR (Dockerfile, .woodpecker.yaml, k8s manifests), (2) infra provisioning (Harbor, Woodpecker, terraform), (3) validation. Route to skill-decompose-ticket.

    Recommendation

    • [BODY] Add explicit file target for westsidekingsandqueens funnel ingress manifest in k8s prod directory -- without it, ArgoCD sync will drop the second hostname.
    • [BODY] Add file target for Stripe-related env vars/secrets in prod manifests (STRIPE_SECRET_KEY, STRIPE_WEBHOOK_SECRET, etc.) -- these are needed for the Stripe features to actually work in prod.
    • [SCOPE] Create project page project-westside-ror in pal-e-docs with user-stories section including ci-pipeline story.
    • [SCOPE] Create architecture note arch-ci-cd documenting the platform CI/CD pipeline pattern.
    • [DECOMPOSE] 10 AC across 2 repos with manual admin operations. Route to skill-decompose-ticket for sub-ticket creation: (1) code changes PR, (2) infra provisioning, (3) end-to-end validation.
  • Verdict: APPROVED

    Re-review after refinement. All [BODY] findings from review-1370-2026-06-06 have been addressed. Remaining items are [SCOPE] infrastructure recommendations only.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Standalone, discovered during session review
    • [x] Repo — ldraney/westside-ror
    • [x] User Story — well-formed (As a parent / I want to register / So that I can complete registration, waiver, and payment)
    • [x] Context — thorough: describes old SvelteKit 4-step flow, current Rails state, existing Registration model columns, existing Parent waiver fields
    • [x] File Targets — present with create/modify/don't-touch sections, all verified accurate
    • [x] Acceptance Criteria — 10 criteria listed
    • [x] Test Expectations — system and unit tests specified with run command
    • [x] Constraints — patterns, references, design language, model reuse constraints specified
    • [x] Checklist — present
    • [x] Related — present (project-westside-ror, westside-app, #39)

    Traceability

    • [x] story:consolidation label — present on board item
    • [ ] story note MISSING — [SCOPE] project-westside-ror note does not exist in pal-e-docs; cannot verify user story entry. Create project page with user-stories section including "consolidation" story. (Carried forward from R1)
    • [x] arch:rails label — present on board item
    • [ ] arch note MISSING — [SCOPE] No arch-rails note found in pal-e-docs. Create architecture note for Rails component. (Carried forward from R1)
    • [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/westside-ror/issues/38, open

    File Targets

    Files to create (verified do not exist yet):

    • [x] app/views/public/register.html.erb — does not exist, directory app/views/public/ confirmed present
    • [x] app/javascript/controllers/registration_progress_controller.js — does not exist; follows existing Stimulus pattern (program_controller.js, inline_fee_controller.js). R1 FIX: now listed in file targets.
    • [x] app/javascript/controllers/age_gate_controller.js — does not exist; follows existing Stimulus pattern. R1 FIX: now listed in file targets.

    Files to modify (verified exist):

    • [x] config/routes.rb — exists (47 lines); no register routes present yet; public routes follow get "name" => "public#action" pattern
    • [x] app/controllers/public_controller.rb — exists (79 lines); no register or create_registration actions yet
    • [x] app/models/registration.rb — exists with belongs_to :tenant and belongs_to :player; correctly listed as "modify" to add validations. R1 FIX: changed from "create" to "modify".
    • [x] app/views/welcome/index.html.erb — exists; hero CTA at line 13 currently links to /teams, confirmed needs change to /register
    • [x] app/assets/stylesheets/application.css — exists (1709 lines)

    Files NOT to touch (verified correct):

    • [x] app/views/public/_interest_form.html.erb — exists, correctly excluded
    • [x] Stripe webhook controller — already working (PR #31 merged)
    • [x] db/schema.rb — registrations table has: amount_cents, payment_status (enum: pending/paid/refunded/failed), player_id, registration_type, signup_method, stripe_checkout_session_id, stripe_payment_intent_id, tenant_id. Parents table has: waiver_signed, waiver_signed_at, waiver_signed_ip. R1 FIX: no spurious migration file target.

    Repo Placement

    OK — issue filed on ldraney/westside-ror, all file targets within that repo. westside-app is read-only source reference.

    Dependencies

    • Stripe webhook infrastructure (PR #31) — merged, in place
    • Existing Stimulus patterns — program_controller.js and inline_fee_controller.js in app/javascript/controllers/
    • Player model has has_many :registrations; Tenant model has has_many :registrations
    • Parent model has waiver fields and has_many :playersR1 FIX: issue now documents that create_registration should update Parent waiver fields
    • Board item #1371 (feature parity audit spike) — same story:consolidation, in backlog. Not blocking.
    • No items in in_progress block this ticket

    Acceptance Criteria

    10 acceptance criteria — assessment:

    • 8 of 10 are objectively verifiable by an agent (route renders, form sections present, age gate toggles, hero CTA link, Stripe redirect, progress bar)
    • "Matching the old SvelteKit flow" — subjective but the 536-line Svelte source serves as reference spec
    • "Mobile-first and matches existing site design" — visual assessment, testable via screenshot comparison
    • Stripe test mode configuration not explicitly documented in constraints — minor gap, agent can infer from existing webhook setup

    Blast Radius

    • Parent model waiver fields — now documented in issue; create_registration will update waiver_signed, waiver_signed_at, waiver_signed_ip
    • Interest form on /teams stays as-is (correctly documented)
    • No other controllers or views reference /register currently
    • Registration model associations (Player has_many, Tenant has_many) must be respected — documented in constraints
    • paymentstatus enum (pending/paid/refunded/failed) already exists in DB — no migration needed for payment_status column

    Decomposition Assessment

    10 AC, 7+ file targets, estimated 15-25 min agent work. Exceeds 5-minute rule thresholds. Previous review flagged [DECOMPOSE]; user acknowledged but explicitly deferred decomposition. Not blocking approval — the issue body is complete and actionable as a single unit of work, though an implementing agent may need extended time.

    R1 Findings Resolution

    R1 Finding Type Status
    registration.rb listed as "create" not "modify" [BODY] RESOLVED — now under "Files to modify"
    Spurious migration file target [BODY] RESOLVED — removed; db/schema.rb listed as "do not touch"
    Missing Stimulus controller file targets [BODY] RESOLVED — both controllers now listed under "Files to create"
    Parent waiver fields undocumented [BODY] RESOLVED — documented in Context and File Targets sections
    Create project-westside-ror page [SCOPE] OPEN — project page still does not exist
    Create arch-rails note [SCOPE] OPEN — architecture note still does not exist
    Decomposition needed [DECOMPOSE] DEFERRED — acknowledged by user, not blocking

    Recommendations

    • [SCOPE] Create project page project-westside-ror with user-stories section including "consolidation" story. (Carried forward from R1 — does not block implementation)
    • [SCOPE] Create architecture note arch-rails for the Rails component. (Carried forward from R1 — does not block implementation)

    No [BODY] or [LABEL] recommendations remain. Issue body is complete and accurate.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Standalone, discovered during session review
    • [x] Repo — ldraney/westside-ror
    • [x] User Story — present and well-formed
    • [x] Context — thorough description of old SvelteKit flow and current state
    • [x] File Targets — present with create/modify/don't-touch sections
    • [x] Acceptance Criteria — 10 criteria listed
    • [x] Test Expectations — system and unit tests specified
    • [x] Constraints — patterns, references, and design language specified
    • [x] Checklist — present
    • [x] Related — present

    Traceability

    • [x] story:consolidation label — present on board item
    • [ ] story note MISSING — [SCOPE] project-westside-ror note does not exist in pal-e-docs; cannot verify user story entry in user-stories section. Create project page with user stories section.
    • [x] arch:rails label — present on board item
    • [ ] arch note MISSING — [SCOPE] No arch-rails note found in pal-e-docs. Create architecture note arch-rails for Rails component.
    • [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/westside-ror/issues/38, open

    File Targets

    • [x] config/routes.rb — verified exists; no register routes present yet, ready for modification
    • [x] app/controllers/public_controller.rb — verified exists; no register/create_registration actions yet
    • [x] app/views/welcome/index.html.erb — verified exists; hero CTA currently links to /teams (line 13), confirmed needs change
    • [x] app/assets/stylesheets/application.css — verified exists; 1709 lines currently
    • [x] app/views/public/_interest_form.html.erb — verified exists; correctly listed as "do not touch"
    • [ ] app/models/registration.rb — ISSUE: issue says "create" but file already exists with belongs_to :tenant and belongs_to :player. The registrations table already exists in the schema with columns: amount_cents, payment_status, player_id, registration_type, signup_method, stripe_checkout_session_id, stripe_payment_intent_id, tenant_id. [BODY] Change from "create" to "modify" and document which columns/validations to add.
    • [ ] db/migrate/*_create_registrations.rb — ISSUE: registrations table already exists. Migration should add missing columns (e.g., waiver fields, promo code) rather than create table. [BODY] Rename to "add registration form fields" migration.
    • [x] app/views/public/register.html.erb — confirmed does not exist yet, correct to create
    • [x] westside-app source files — verified: +page.svelte (536 lines) and app.css both exist at referenced paths

    Repo Placement

    OK — issue is filed on ldraney/westside-ror and all file targets are within that repo. The westside-app reference is read-only source material, not a target.

    Dependencies

    • Stripe webhook infrastructure (PR #31) — merged, in place. Webhook controller handles checkout.session.completed.
    • Existing Stimulus patterns — program_controller.js and inline_fee_controller.js provide patterns to follow.
    • Board item #1371 (Spike: audit westside-app vs westside-ror feature parity) — same story:consolidation label, in backlog. Not a blocking dependency but related; the audit spike would inform whether this registration port is complete.
    • No items in in_progress block this ticket.
    • Player model already has has_many :registrations association.
    • Parent model has waiver_signed, waiver_signed_at, waiver_signed_ip fields — waiver may need to update parent record, not just registration.

    Acceptance Criteria

    10 acceptance criteria — mostly verifiable by an agent, but some concerns:

    • "Matches the old SvelteKit flow" — subjective without pixel-level spec; the 536-line Svelte source is the reference but visual parity is hard to test automatically.
    • "Mobile-first and matches existing site design" — again subjective; testable only via screenshot comparison.
    • Stripe checkout redirect testing requires Stripe test mode configuration — not specified in constraints.
    • "Progress bar tracks current section via scroll position" — requires a new Stimulus controller (not listed in file targets). [BODY] Add app/javascript/controllers/registration_progress_controller.js to file targets.
    • "Age gate toggles" — requires another Stimulus controller. [BODY] Add app/javascript/controllers/age_gate_controller.js to file targets.

    Blast Radius

    • Parent model has waiver fields (waiver_signed, waiver_signed_at, waiver_signed_ip) — registration waiver should update these. Not mentioned in issue.
    • The interest form on /teams stays as-is (correctly noted in issue).
    • No other controllers or views reference /register currently.
    • The registrations table is already referenced by Player (has_many) and Tenant (has_many) — new registration creation flow must respect these associations.

    Decomposition Assessment

    NEEDS DECOMPOSITION — route to skill-decompose-ticket.

    • 10 acceptance criteria (threshold: >5)
    • 7+ file targets to create/modify across views, models, controllers, JS, CSS, migrations, routes
    • Estimated agent work: 15-25 minutes (port 536-line Svelte component, port ~500 lines CSS, create 2+ Stimulus controllers, add model validations, add routes, add controller actions, write tests)
    • Natural decomposition into: (1) route + controller + basic view, (2) form UI with Stimulus controllers, (3) waiver section, (4) Stripe payment integration, (5) CSS port, (6) hero CTA update + tests

    Recommendations

    • [BODY] Fix file target: app/models/registration.rb — change from "create" to "modify"; document which validations and columns to add to existing model/table.
    • [BODY] Fix file target: db/migrate/*_create_registrations.rb — rename to "add registration form fields" migration since table already exists.
    • [BODY] Add missing file targets: app/javascript/controllers/registration_progress_controller.js and app/javascript/controllers/age_gate_controller.js (or a combined registration_controller.js).
    • [BODY] Document that parent model waiver fields (waiver_signed, waiver_signed_at, waiver_signed_ip) should be updated during registration waiver step.
    • [SCOPE] Create project page project-westside-ror with user-stories section including "consolidation" story.
    • [SCOPE] Create architecture note arch-rails for the Rails component.
    • [DECOMPOSE] 10 AC, 7+ files, estimated 15-25 min agent work. Route to skill-decompose-ticket for sub-ticket creation.
  • Verdict: READY

    Spike scope is solid. The question is clear, the time-box is appropriate, both codebases exist locally for comparison, and the deliverable (audit checklist, no code) fits a single agent pass. The traceability gaps below are pre-existing project-level issues already flagged in prior reviews — they do not block this spike.

    Template Completeness

    • [x] Type — "Spike"
    • [x] Lineage — "Standalone — discovered during session when the registration form was found missing"
    • [x] Repo — ldraney/westside-ror
    • [x] Question — Clear top-level question with 6 sub-areas (routes, forms, content, styles, JS behavior, API integrations)
    • [x] Deliverables — Present, describes checklist output. Minor format deviation: uses paragraph instead of template's checkbox format with explicit docs/{topic}.md artifact, but intent is equivalent for a spike that produces findings rather than an architecture doc.
    • [x] Time-box — "2 hours"
    • [x] Related — References #38 (registration form port) and project-westside-ror
    • [x] Checklist — Extra section not in template, but harmless (duplicates Deliverables intent)

    Traceability

    • [x] story:consolidation label — present on board item
    • [ ] story note MISSING — project-westside-ror project page does not exist in pal-e-docs, so user story cannot be verified. This is a known gap flagged in review-1367-2026-06-06 and review-1367-2026-06-06-r2. [SCOPE] Create project-westside-ror project page with user-stories section.
    • [x] arch:rails label — present on board item
    • [ ] arch note MISSING — no arch-rails note exists in pal-e-docs. However, "rails" is the framework itself, not a custom architecture component. For a spike that audits feature parity, this is acceptable — no custom architecture decision document is expected. [SCOPE] Consider whether arch-rails warrants a dedicated note or should be renamed to a more specific component label.
    • [x] Forgejo issue — #39, open, body well-formed

    File Targets

    N/A — spike type. No file targets expected. The issue correctly does not list file targets.

    However, verified that both codebases are available locally for the audit agent:

    • [x] /home/ldraney/westside-ror/ — Rails app with 7 public routes, admin namespace, webhooks, emails
    • [x] /home/ldraney/westside-app/ — SvelteKit app (archived) with ~35 route files across (app) and (public) groups
    • [x] /home/ldraney/westside-ror/docs/consolidation.md — existing consolidation doc documents what was killed, what survives, and open items

    Repo Placement

    OK. Issue filed on ldraney/westside-ror which is the surviving app. The spike needs to read both westside-app (archived SvelteKit) and westside-ror (Rails), but both are available locally. No cross-repo issue needed — the audit output belongs in westside-ror.

    Dependencies

    • #38 (Port multi-step registration form) — board item 1370, backlog, 8 points. This is a known gap that prompted the spike. The spike should discover this and potentially other gaps. No blocking dependency — the spike informs #38, not the other way around.
    • #33 (Consolidation spike) — closed, done. Prior spike that drove the consolidation. This spike is a follow-up audit after consolidation is complete.
    • No blockers — no items in in_progress or next_up block this spike. The two in_progress items (#2 tournament payments, #13 Queens Phoenix trip) are unrelated.

    Acceptance Criteria

    The issue uses a "Checklist" section with 2 items: (1) Audit complete, (2) Findings documented. These are appropriate for a spike — the deliverable is knowledge, not code. An agent can verify completion by checking whether the audit findings exist in issue comments or a linked note.

    The "Deliverables" section adds specificity: "A checklist of inconsistencies with severity (missing feature vs. cosmetic difference vs. content drift), grouped by page/route." This is testable — the output must be a structured checklist with severity classification.

    Blast Radius

    Low. This is a read-only audit — no code changes. The findings will generate follow-up tickets. No downstream consumers are affected by the spike itself.

    Note: the westside-app repo is archived on Forgejo, so the local copy at /home/ldraney/westside-app/ is the source of truth for the audit. The agent should not attempt to clone or pull from the archived remote.

    Decomposition Assessment

    No decomposition needed. Single repo, 2-hour time-box, read-only audit. The deliverable is a checklist document, not code changes. Well within a single agent pass. 0 file targets to modify, 2 acceptance criteria, estimated agent work under 5 minutes for the review (the spike execution itself is time-boxed at 2 hours but that is execution, not scope review).

    Recommendation

    • [SCOPE] Create project-westside-ror project page with user-stories section — pre-existing gap, not blocking this spike.
    • [SCOPE] Evaluate whether arch-rails needs a dedicated architecture note or if the label should reference something more specific (e.g., arch-westside-ror) — pre-existing gap, not blocking this spike.

    No action needed to unblock this spike. READY to move to next_up.

  • Verdict: APPROVED

    Re-review after NEEDS_REFINEMENT. The issue body was substantially rewritten to address all critical findings from review-1367-2026-06-06. The two blocking recommendations ([BODY] and [LABEL]) are resolved. Two [SCOPE] items remain but are not blockers for this ticket's execution.

    Previous Review Findings — Resolution Status

    • [x] [BODY] Add k3s.tfvars to file targets — RESOLVED. Now the primary file target with specific line references for both services and clients blocks.
    • [x] [LABEL] Change story:admin-roster-report to story:consolidation — RESOLVED. Board item #1367 now has label story:consolidation.
    • [ ] [SCOPE] Create project-westside-ror project page — STILL MISSING. No project-westside-ror note exists in pal-e-docs. Not a blocker for ticket execution; the story label is meaningful without it.
    • [ ] [SCOPE] Create arch-westside-ror architecture note — STILL MISSING. No arch-westside-ror note exists in pal-e-docs. Not a blocker for this infra teardown ticket.
    • [x] [DECOMPOSE] Previous review recommended decomposition — REASSESSED below. The rewritten issue correctly reframes all work as terraform-driven, making it a single coordinated operation rather than 5 separate system touches.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — references #33 consolidation spike, #35, #36 as prerequisites
    • [x] Repo — correctly identifies pal-e-services (terraform), pal-e-deployments (overlays), Forgejo admin (archival)
    • [x] User Story — clear operator perspective
    • [x] Context — explains terraform as source of truth, why manual kubectl is wrong
    • [x] File Targets — specific files with line references, plus explicit "do NOT touch" list
    • [x] Acceptance Criteria — 12 items
    • [x] Test Expectations — 5 verification commands
    • [x] Constraints — terraform-first, review plan before apply, archive-not-delete, keep realm
    • [x] Checklist — two PRs (pal-e-services, pal-e-deployments) + tofu plan output
    • [x] Related — links to project and prerequisite issues

    Traceability

    • [x] story:consolidation label — matches decommission/cleanup scope
    • [ ] story note NOT verified — no project-westside-ror note with user-stories section exists. [SCOPE] Create project page project-westside-ror with user-stories section (deferred, not a blocker).
    • [x] arch:westside-ror label — present on board item
    • [ ] arch note NOT verified — no arch-westside-ror note exists. [SCOPE] Create architecture note arch-westside-ror (deferred, not a blocker).
    • [x] Forgejo issue — #37, open, valid

    File Targets

    • [x] pal-e-services/terraform/k3s.tfvars — verified exists (10k). basketball-api service block at lines 200-208. westsidekingsandqueens service block at lines 209-217. Both confirmed present.
    • [x] pal-e-services/terraform/k3s.tfvars clients — westside-landing block (HCL key) at lines 73-89 with client_id = "westside-app". westside-spa block at lines 90-109. Both confirmed present.
    • [x] pal-e-deployments/overlays/basketball-api/ — verified exists (prod/ subdirectory with postgres.yaml, secrets, etc.)
    • [x] pal-e-deployments/overlays/westsidekingsandqueens/ — verified exists (dev/ and prod/ subdirectories)

    Minor line reference inaccuracies (not blocking):

    • Issue says basketball-api at lines 200-207 — actual block includes cmp_plugin field, runs to line 208
    • Issue says westsidekingsandqueens at lines 209-216 — actual block runs to line 217
    • Issue says westside-app at lines 74-89 — the HCL key is westside-landing (line 73), inner content 74-89 is correct. Agent implementing this should remove the entire block including the key line.
    • Issue says westside-spa at lines 91-107 — actual block starts at line 90, includes extra http://localhost web_origin, closing brace at line 109

    These are off-by-one or off-by-two errors typical of line references. The blocks are unambiguously identifiable by their HCL keys and the agent will handle them correctly.

    Repo Placement

    Issue filed on ldraney/westside-ror. Work spans pal-e-services (terraform) and pal-e-deployments (overlay cleanup). The issue correctly identifies both repos and specifies separate PRs for each. Filing on westside-ror is acceptable since this is the consolidation project's home repo and the ticket coordinates cross-repo work.

    Dependencies

    • [x] #35 (data migration) — board item 1365, column: done. Prerequisite satisfied.
    • [x] #36 (URL cutover) — board item 1366, column: done. Prerequisite satisfied.
    • [x] #33 (consolidation spike) — board item 1362, column: done. Parent work complete.
    • [x] #7 (Keycloak auth) — correctly noted as unblocked by this work; realm is preserved.
    • No blocking items in in_progress.

    Acceptance Criteria

    12 acceptance criteria. Assessment:

    • AC 1-4 (remove tfvars entries) — directly verifiable by diffing the PR
    • AC 5-6 (tofu plan/apply) — verifiable by operator running commands; plan output included in PR per checklist
    • AC 7 (kubectl get ns) — verifiable post-apply
    • AC 8-9 (repo archival) — manual Forgejo admin action, verifiable
    • AC 10 (Tailscale nodes) — issue now correctly notes "may happen automatically via funnel teardown." Acceptable.
    • AC 11 (PVC noted) — informational, not a pass/fail criterion. Backup location documented.
    • AC 12 (overlay directories removed) — verifiable via PR diff

    All criteria are clear and verifiable. The previous concern about Tailscale being unautomatable is addressed by the "may happen automatically" qualifier.

    Blast Radius

    • pal-e-services docsREADME.md (line 65), CLAUDE.md (line 12), SERVICE_ONBOARDING.md (17+ references) all mention basketball-api as examples. These are documentation references, not functional. Low priority cleanup, not required for this ticket.
    • pal-e-deployments/overlays/dev-tunnel/README.md — line 27 references basketball-api.basketball-api.svc.cluster.local:8000. Minor doc cleanup.
    • pal-e-deployments/overlays/westside-ror/README.md — line 3 mentions "replacing basketball-api and westsidekingsandqueens." Historical reference, no action needed.
    • No code-level blast radius — terraform handles all resource teardown cleanly.

    Decomposition Assessment

    Previous review recommended decomposition (9 AC across 5 systems). Reassessment: No decomposition needed.

    The rewritten issue correctly reframes the work as terraform-driven:

    • PR 1: Edit k3s.tfvars (remove 4 blocks from 1 file), run tofu plan, include output — single agent pass, <5 min
    • PR 2: git rm two overlay directories in pal-e-deployments — trivial, <2 min
    • Manual steps: tofu apply (operator), repo archival (operator), Tailscale cleanup (may auto-resolve) — these are operator actions, not agent work

    The agent work is 2 simple PRs touching 3 files total. Well within the 5-minute rule. The 12 AC count is high but most are verification steps after tofu apply, not implementation work.

    Recommendation

    • [SCOPE] Create project page project-westside-ror with user-stories section (deferred — does not block ticket execution).
    • [SCOPE] Create architecture note arch-westside-ror (deferred — does not block ticket execution).

    No [BODY], [LABEL], or [DECOMPOSE] recommendations. Ticket is ready for implementation.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — references #33 consolidation spike
    • [x] Repo — specifies pal-e-deployments
    • [x] User Story
    • [x] Context — describes what's no longer needed after #35/#36
    • [x] File Targets — pal-e-deployments overlays + Forgejo repo archival
    • [x] Acceptance Criteria — 9 items
    • [x] Test Expectations — kubectl verification commands
    • [x] Constraints — prerequisite gates, PVC retention, archive-not-delete
    • [x] Checklist
    • [x] Related — links to #33, #35, #36

    Traceability

    • [ ] story:admin-roster-report label — MISMATCH. This is a namespace decommission ticket, not part of the admin roster report story. The story label should reflect the consolidation effort (e.g., story:consolidation or story:infra-cleanup). [LABEL] Change story label to match actual work scope.
    • [ ] story note MISSING — No project page project-westside-ror exists in pal-e-docs to verify any story entry. [SCOPE] Create project page project-westside-ror with user-stories section.
    • [ ] arch note MISSING — arch:westside-ror label present but no arch-westside-ror note found in pal-e-docs. [SCOPE] Create architecture note arch-westside-ror.
    • [x] Forgejo issue — #37, open, valid

    File Targets

    • [x] pal-e-deployments/overlays/basketball-api/ — verified: exists (6 files including kustomization.yaml, deployment-patch.yaml, postgres.yaml, pvc.yaml, secrets)
    • [x] pal-e-deployments/overlays/westsidekingsandqueens/ — verified: exists (prod: 4 files, dev: 4 files)
    • [x] westside-ror codebase — verified: only doc references (README.md, docs/db-reconciliation.md), no code changes needed. Correct.
    • [ ] pal-e-services/terraform/k3s.tfvars — MISSING from file targets. Contains basketball-api service entry (lines 200-206), westsidekingsandqueens service entry (lines 209-215), and westside-spa Keycloak client (lines 90-105 with redirect URIs). If overlays are deleted but terraform entries remain, terraform apply will fail or attempt to recreate resources. [BODY] Add pal-e-services/terraform/k3s.tfvars to file targets: remove basketball-api + westsidekingsandqueens service entries and westside-spa Keycloak client block.

    Repo Placement

    Issue filed on ldraney/westside-ror but all work happens in pal-e-deployments (manifest removal), pal-e-services (terraform cleanup), Forgejo admin (repo archival), Keycloak admin, and Tailscale admin console. The issue body correctly identifies pal-e-deployments as the repo, but the Forgejo issue itself is on westside-ror. Acceptable since this is the consolidation project's repo and the PR checklist says "PR opened (on pal-e-deployments)." However, pal-e-services is also affected (see file targets).

    Dependencies

    • [x] #35 (data migration) — board item 1365, column: done. Prerequisite satisfied.
    • [x] #36 (URL cutover) — board item 1366, column: done. Prerequisite satisfied.
    • [x] #33 (consolidation spike) — board item 1362, column: done. Parent work complete.
    • No blocking items in in_progress for this work.

    Acceptance Criteria

    9 acceptance criteria. Most are verifiable by an agent via kubectl commands. Concerns:

    • "Orphaned Tailscale nodes cleaned up in Tailscale admin console" — requires Tailscale admin UI access, not automatable by an agent. Manual step.
    • "Sidecar postgres PVC preserved for 30 days" — this is a deferred action, not verifiable at implementation time. Needs a follow-up ticket or calendar reminder for day-30 deletion.
    • "Keycloak westside-spa client disabled or deleted" — needs to specify whether this is via Keycloak admin UI or terraform (answer: terraform, since it's in k3s.tfvars).

    Blast Radius

    • pal-e-services/terraform — The services map in k3s.tfvars drives Argo CD applications, Harbor robot accounts, and Kubernetes namespaces via terraform. Removing overlays without removing the terraform entries will cause plan/apply drift or failures. This is the most critical gap.
    • pal-e-deployments README and dev-tunnel README reference these namespaces — minor doc cleanup.
    • westside-ror README/docs — historical references only, no action needed.
    • pal-e-services README, SERVICE_ONBOARDING.md, CLAUDE.md — reference basketball-api, minor doc cleanup.

    Decomposition Assessment

    9 AC across 5 systems (k8s cluster, pal-e-deployments, pal-e-services/terraform, Forgejo admin, Tailscale admin). Estimated agent work exceeds 5 minutes. However, the nature of this work is mostly imperative commands (kubectl delete, git rm, terraform changes) rather than code authoring. Some steps (Tailscale admin, PVC retention scheduling) are manual and cannot be agent-executed. Recommendation: NEEDS DECOMPOSITION — split into: (1) terraform cleanup PR on pal-e-services, (2) overlay removal PR on pal-e-deployments, (3) manual checklist for Forgejo archival + Tailscale cleanup + PVC retention timer. Route to skill-decompose-ticket.

    Recommendation

    • [BODY] Add pal-e-services/terraform/k3s.tfvars to File Targets — remove basketball-api service entry, westsidekingsandqueens service entry, and westside-spa Keycloak client block.
    • [LABEL] Change story:admin-roster-report to story:consolidation or story:infra-cleanup — current label is a mismatch for decommission work.
    • [SCOPE] Create project page project-westside-ror with user-stories section.
    • [SCOPE] Create architecture note arch-westside-ror.
    • [DECOMPOSE] 9 AC across 5 systems. Route to skill-decompose-ticket for sub-ticket creation: (1) pal-e-services terraform cleanup, (2) pal-e-deployments overlay removal, (3) manual ops checklist (Forgejo archival, Tailscale nodes, PVC retention timer).
  • Review 2: Commit prod deployment manifests review-1358-2026-06-06-v2

    Verdict: READY

    Re-review of board item #1358 after refinement. Previous review review-1358-2026-06-06 returned NEEDS_REFINEMENT with 3 [BODY] fixes and 1 [SCOPE] recommendation. All 3 body fixes have been addressed. The [SCOPE] item (missing project page) is pre-existing platform documentation debt, not a ticket scope problem.

    Previous Recommendations Resolved

    1. [BODY] Clarify service.yaml file target -- FIXED: removed ambiguous service.yaml entirely. File targets now list only kustomization.yaml and deployment-patch.yaml.
    2. [BODY] Add kubectl command for live spec capture -- FIXED: issue now includes kubectl get deployment westside-ror -n westside-ror -o yaml > /tmp/westside-ror-live.yaml.
    3. [BODY] Add explicit bases/standard requirement -- FIXED: issue now states "The prod overlay MUST use bases/standard (not standalone resources like the dev overlay)" and references basketball-api/prod/kustomization.yaml as canonical example.
    4. [SCOPE] Create project-westside-ror project page -- NOT ADDRESSED (human decision, carried forward as non-blocking).

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- spike #22, prerequisite for #27
    • [x] Repo -- ldraney/pal-e-deployments (primary), ldraney/westside-ror (tracking)
    • [x] User Story -- present and well-formed
    • [x] Context -- thorough, includes live vs committed comparison with specific env vars and patterns
    • [x] File Targets -- 2 files to create, clear do-not-touch list, no ambiguity
    • [x] Acceptance Criteria -- 7 items, all testable
    • [x] Test Expectations -- kustomize build command provided, diff against live deployment
    • [x] Constraints -- explicit bases/standard requirement, references canonical example
    • [x] Checklist -- present
    • [x] Related -- lists project and related issues (#27, #24)

    All required Feature template sections present and complete.

    Traceability

    • [x] story:subscription-links label -- present on board item
    • [ ] story note MISSING -- no project-westside-ror project page exists in pal-e-docs. Acceptable: user story is fully stated inline in the issue body. Per precedent (review-1356-2026-06-06), not blocking. [SCOPE] Create project-westside-ror project page with user-stories section (low priority, carried from v1).
    • [x] arch:kustomize label -- present on board item
    • [x] arch note verified -- convention-kustomize-overlay exists in pal-e-docs (convention note; kustomize overlays are a convention, not a diagrammable arch component, per precedent review-1044-2026-04-21)
    • [x] Forgejo issue -- ldraney/westside-ror#28, open

    File Targets

    • [x] overlays/westside-ror/prod/kustomization.yaml -- verified: directory does NOT exist yet (correct, file to create). bases/standard/ exists with deployment.yaml, service.yaml, hpa.yaml, kustomization.yaml, networkpolicy.yaml, servicemonitor.yaml. Reference pattern basketball-api/prod/kustomization.yaml verified: uses ../../../bases/standard, JSON patches to rename app, deployment-patch.yaml.
    • [x] overlays/westside-ror/prod/deployment-patch.yaml -- verified: file to create. Reference pattern basketball-api/prod/deployment-patch.yaml verified: shows env vars, secretRef, container spec patches.
    • [x] overlays/westside-ror/dev/ -- verified: exists with deployment.yaml, service.yaml, kustomization.yaml, ingress.yaml. Issue correctly says "do not touch".

    Repo Placement

    OK. Issue filed on ldraney/westside-ror (tracking), work targets ldraney/pal-e-deployments (primary). Explicitly documented in Repo section. PR should be opened against pal-e-deployments.

    Dependencies

    • Upstream: spike #22 (done) -- no blocker
    • Downstream: #27 (Stripe webhook needs STRIPE_WEBHOOK_SECRET in deployment config) -- this ticket is a prerequisite. Documented.
    • Downstream: #24 (editable fees, safe deploys needed) -- documented.
    • Board state: No blockers in in_progress. Items #2 and #13 are in progress but unrelated (tournament payments/email).

    Acceptance Criteria

    7 criteria, all verifiable:

    • [x] AC1: prod/ overlay exists -- verifiable with ls
    • [x] AC2: kustomization.yaml references ../../../bases/standard -- verifiable with grep
    • [x] AC3: JSON patches rename base app -- verifiable in YAML
    • [x] AC4: deployment-patch.yaml matches live pod spec -- verifiable with kubectl diff (issue now provides capture command)
    • [x] AC5: kustomize build produces valid YAML -- verifiable with command
    • [x] AC6: diff between build output and live deployment shows no meaningful divergence -- verifiable with diff
    • [x] AC7: Comment noting STRIPE_WEBHOOK_SECRET for #27 -- verifiable with grep

    All criteria are testable by an agent with cluster access.

    Blast Radius

    Low. This is a "commit what's running" task -- no behavior change. The committed manifests will match the live pod. No sibling services affected. Dev overlay is explicitly excluded.

    Decomposition Assessment

    2 file targets in 1 repo (pal-e-deployments). 7 acceptance criteria (slightly above the 5 threshold but all are closely related verification steps for the same 2 files). Estimated agent work: ~3 minutes. No decomposition needed.

    Recommendations

    1. [SCOPE] Create project-westside-ror project page in pal-e-docs with a user-stories section containing the subscription-links story (carried from v1, non-blocking, low priority).

    No [BODY] or [LABEL] fixes needed. Ticket is ready for implementation.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- spike #22, prerequisite for #27
    • [x] Repo -- ldraney/pal-e-deployments (primary), ldraney/westside-ror (tracking)
    • [x] User Story -- present and well-formed
    • [x] Context -- thorough, includes live vs committed comparison
    • [x] File Targets -- present, lists create and do-not-touch files
    • [x] Acceptance Criteria -- 5 items, all testable
    • [x] Test Expectations -- kustomize build command provided
    • [x] Constraints -- present and clear
    • [x] Checklist -- present
    • [x] Related -- lists project and related issues

    All required Feature template sections present.

    Traceability

    • [x] story:subscription-links label -- present on board item
    • [ ] story note MISSING -- no project-westside-ror project page exists in pal-e-docs at all. [SCOPE] Create project-westside-ror project page with user-stories section.
    • [x] arch:kustomize label -- present on board item
    • [x] arch note verified -- convention-kustomize-overlay exists in pal-e-docs (convention note, not arch note, but this is correct -- kustomize overlays are a convention, not a diagrammable arch component per prior review precedent review-1044-2026-04-21)
    • [x] Forgejo issue -- ldraney/westside-ror#28, open

    File Targets

    • [x] overlays/westside-ror/prod/ -- verified: directory does NOT exist yet (correct, these are files to create)
    • [x] overlays/westside-ror/dev/ -- verified: exists with deployment.yaml, service.yaml, kustomization.yaml, ingress.yaml. Issue correctly says "do not touch"
    • [ ] File targets list service.yaml with "if different from dev" qualifier -- ISSUE: the issue should clarify whether the prod service differs. Based on the basketball-api and believers-elite patterns, prod overlays use the bases/standard Service (renamed via JSON patch) rather than a standalone service.yaml. The agent needs explicit guidance. [BODY] Clarify whether service.yaml is needed or if the base Service (renamed via JSON patch in kustomization.yaml) suffices -- per platform convention, prod overlays use bases/standard not standalone resources.

    Repo Placement

    The issue is filed on ldraney/westside-ror but the work is in ldraney/pal-e-deployments. The issue acknowledges this explicitly (Repo section says "primary: pal-e-deployments, tracking: westside-ror"). This is acceptable -- the tracking issue pattern is established practice. However, the PR should be opened against pal-e-deployments, not westside-ror.

    Dependencies

    • Upstream: spike #22 (done per memory) -- no blocker
    • Downstream: #27 (Stripe webhook needs STRIPE_WEBHOOK_SECRET in deployment config) -- this ticket is a prerequisite. Documented in issue.
    • Downstream: #24 (editable fees, safe deploys needed) -- documented in issue.
    • Board state: No blockers in in_progress. Items #2 and #13 are in progress but unrelated (tournament payments/email).

    Acceptance Criteria

    • [x] AC1: prod/ overlay exists -- verifiable with ls
    • [x] AC2: deployment patch matches live pod spec -- verifiable with diff
    • [x] AC3: kustomize build produces valid YAML -- verifiable with command
    • [ ] AC4: "deploying from committed manifests produces same pod spec as live" -- ISSUE: this is functionally a superset of AC2+AC3 but harder to verify without cluster access. The agent would need kubectl get deployment -o yaml output to diff against. The issue provides a good description of what's running (Context section) but the agent needs the actual live spec to diff. [BODY] Add a note that the live spec can be captured via kubectl get deployment westside-ror -n westside-ror -o yaml and should be diffed against kustomize build output.
    • [x] AC5: STRIPE_WEBHOOK_SECRET placeholder -- verifiable in the YAML

    Blast Radius

    Low blast radius. This is a "commit what's running" task -- no behavior change. The only risk is if the committed manifests don't match reality and someone syncs ArgoCD, but that's exactly what this ticket prevents. No sibling services affected.

    Note: the dev overlay uses a standalone structure (no bases/standard reference), while the prod overlay should use bases/standard per platform convention. This is not a blast radius concern but an implementation detail the agent must get right.

    Decomposition Assessment

    3 file targets in 1 repo (pal-e-deployments). 5 acceptance criteria. Estimated agent work: ~3 minutes. No decomposition needed. Well within the 5-minute rule.

    Recommendations

    1. [SCOPE] Create project-westside-ror project page in pal-e-docs with a user-stories section containing the subscription-links story.
    2. [BODY] Clarify the service.yaml file target: per platform convention (convention-kustomize-overlay), prod overlays reference bases/standard and rename via JSON patch. Remove "service.yaml -- if different from dev" and replace with guidance to use the base Service renamed via JSON patch, matching basketball-api/believers-elite patterns.
    3. [BODY] Add instruction for the agent to capture the live spec: kubectl get deployment westside-ror -n westside-ror -o yaml > /tmp/live-spec.yaml for diffing against kustomize build output.
    4. [BODY] Add explicit note that the prod overlay must use bases/standard (unlike the dev overlay which uses standalone resources). Reference basketball-api and believers-elite as patterns.
  • Verdict: APPROVED

    Re-review of board item #1357 (Forgejo issue ldraney/westside-ror#27). Previous review review-1357-2026-06-06 returned NEEDS_REFINEMENT with 3 [BODY] and 2 [SCOPE] recommendations. All [BODY] items have been addressed in the updated issue. [SCOPE] items remain outstanding but are platform-level documentation tasks that do not block implementation.

    Previous Findings Disposition

    • [x] [BODY] Graceful handling of missing metadata -- FIXED. New AC #6 explicitly requires graceful handling of events with missing or unrecognized metadata. Context section now documents that Marcus created some payment links outside the app. Test Expectation #8 covers this case.
    • [x] [BODY] Explicit idempotency AC -- FIXED. New AC #10: "Idempotent: processing the same event twice produces the same result without errors." Test Expectation #9: "processing the same event twice does not error or create duplicate state." Constraint section also restates this.
    • [x] [BODY] Clarify #23 closed -- FIXED. Issue #23 is confirmed closed on Forgejo. Related section in #27 now states "(closed, replaced by #26 + #27)."
    • [ ] [SCOPE] Create project-westside-ror with user-stories section -- STILL OUTSTANDING. Not a blocker for implementation.
    • [ ] [SCOPE] Create arch-rails note in pal-e-docs -- STILL OUTSTANDING. Not a blocker for implementation. In-repo architecture doc docs/stripe-subscription-architecture.md covers the webhook design in detail (Flow 3).

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- split from #23 (Sub-B: webhook + status sync)
    • [x] Repo -- ldraney/westside-ror
    • [x] User Story -- system actor, auto-updates roster report
    • [x] Context -- explains gap, documents data format, addresses external links
    • [x] File Targets -- 4 modify/create, 3 do-not-touch
    • [x] Acceptance Criteria -- 10 criteria (up from 8, added graceful handling + idempotency)
    • [x] Test Expectations -- 9 test cases + run command (up from 7, added missing-metadata + idempotency tests)
    • [x] Constraints -- 5 constraints listed (includes idempotency)
    • [x] Checklist -- standard 3-item
    • [x] Related -- references project, companion tickets, spike, arch doc; #23 confirmed closed

    Traceability

    • [x] story:subscription-links label -- present on board item
    • [ ] story note -- project-westside-ror does not exist in pal-e-docs (404). Carried forward as non-blocking [SCOPE] item.
    • [x] arch:rails label -- present on board item
    • [ ] arch note -- no arch-rails note in pal-e-docs. In-repo doc docs/stripe-subscription-architecture.md covers webhook design (Flow 3). Carried forward as non-blocking [SCOPE] item.
    • [x] Forgejo issue -- ldraney/westside-ror#27, state: open

    File Targets

    • [x] config/routes.rb -- verified: exists (39 lines), no webhook route currently. Route addition straightforward.
    • [x] app/controllers/webhooks_controller.rb -- correctly marked as NEW. Does not exist. No existing skip_forgery_protection patterns in codebase; new controller will need it.
    • [x] app/services/stripe_client.rb -- verified: exists (127 lines). Uses raw net/http, no Stripe gem. Adding verify_webhook_signature is consistent with existing patterns.
    • [x] Deployment config (env var STRIPE_WEBHOOK_SECRET) -- no existing references. New secret needed.
    • [x] DB columns verified in schema.rb:
      • payment_links.paid_at (line 216) -- datetime, nullable
      • payment_links.stripe_payment_link_id (line 221) -- string, not null
      • players.stripe_subscription_id (line 267) -- string, nullable
      • players.subscription_status (line 268) -- enum (active, past_due, canceled, none), default none
    • [x] Enum values match AC: subscriptionstatus = [active, past_due, canceled, none] (schema line 37)

    Repo Placement

    OK. Issue filed on ldraney/westside-ror, all file targets in that repo. Single-repo change.

    Dependencies

    • #24 (editable fees) -- no blocking dependency. Webhook does not require fee editing.
    • #26 (copy links UI) -- companion ticket, explicitly independent.
    • #22 (spike) -- closed. Produced architecture doc.
    • #23 (original combined) -- confirmed closed on Forgejo. Housekeeping resolved.
    • No items in in_progress block this ticket.

    Acceptance Criteria

    All 10 criteria are testable by an agent. Test expectations map cleanly to AC (9 test cases cover all 10 AC). Run command (rails test) is valid. The two new additions (graceful handling AC #6, idempotency AC #10) close the gaps from the previous review.

    Blast Radius

    • subscription_status read by admin/roster_report_controller.rb (lines 14, 24, 29, 30). Webhook updates surface automatically in roster report -- desired effect, no code changes needed.
    • paid_at read by StripeClient#db_payment_summary (lines 70, 84), used by payments dashboard. Same -- desired effect.
    • skip_forgery_protection scoped to new WebhooksController only. No global impact.
    • No other controllers or services reference these columns.

    Decomposition Assessment

    4 file targets in 1 repo. 10 AC, 9 test cases. Estimated agent work: ~8-10 minutes. Above the 5-minute threshold but all targets are tightly coupled in one repo -- decomposing would create artificial seams. Single agent pass is appropriate. No decomposition needed.

    Recommendations

    No blocking recommendations. Two carried-forward [SCOPE] items for platform documentation:

    • [SCOPE] Create project page project-westside-ror with user-stories section including subscription-links story. (Non-blocking)
    • [SCOPE] Create architecture note arch-rails in pal-e-docs, or reconsider label as arch:stripe. In-repo doc exists at docs/stripe-subscription-architecture.md. (Non-blocking)
  • Verdict: READY

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Split from #23, depends on #24
    • [x] Repo — ldraney/westside-ror
    • [x] User Story — Marcus (admin) copy-to-clipboard workflow
    • [x] Context — Thorough: spike #22 findings, existing state, dependency chain
    • [x] File Targets — 3 files to modify, 2 exclusions documented
    • [x] Acceptance Criteria — 7 criteria, all testable
    • [x] Test Expectations — 2 unit tests, 1 integration test, run command
    • [x] Constraints — Product reuse, raw net/http pattern, cents conversion
    • [x] Checklist — Present
    • [x] Related — Links to project, prerequisite, spike, and arch doc

    Traceability

    • [x] story:subscription-links label — present on board item
    • [ ] story note MISSING — no project-westside-ror project page exists in pal-e-docs; no user-stories section to verify. Acceptable: the user story is fully stated inline in the issue body. [SCOPE] Create project page project-westside-ror with user-stories section (low priority, not blocking).
    • [x] arch:rails label — present on board item
    • [ ] arch note MISSING — search for arch-rails returned no results. Acceptable: "rails" is the application framework, not a discrete architecture component. The actual architecture doc exists at docs/stripe-subscription-architecture.md in the repo and is referenced in the issue. [SCOPE] Consider creating arch-stripe note in pal-e-docs to back the scope:stripe label (low priority, not blocking).
    • [x] Forgejo issue — ldraney/westside-ror#26, open

    File Targets

    • [x] app/services/stripe_client.rb — verified: exists (3.6k). Currently has only GET methods (paginate, get). Adding create_subscription_link requires a new private post method. Correct target.
    • [x] app/controllers/admin/roster_report_controller.rb — verified: exists (1.3k). Currently loads teams/players with includes(players: :parent). Will need to add :payment_links to the includes and instantiate StripeClient. Correct target.
    • [x] app/views/admin/roster_report/index.html.erb — verified: exists (4.0k). Currently renders player rows with name/parent/phone/email/monthly/status columns. "Copy Link" button + JS clipboard logic will be added here. Correct target.
    • [x] Exclusions verified — payments_controller.rb and webhooks_controller.rb correctly excluded (separate concerns).

    Repo Placement

    Correct. Issue filed on ldraney/westside-ror, all file targets are in the same repo. Single-repo change.

    Dependencies

    • #24 (inline-editable monthly fee) — documented as prerequisite, confirmed still open. Payment link amount derives from player.monthly_fee. Correctly identified. Board item #1334 is in backlog.
    • #27 (Stripe webhook) — correctly split out as separate ticket. Board item #1357 is in backlog.
    • #23 (original combined ticket) — parent ticket, board item #1335 still in backlog. Should be closed/superseded once #26 and #27 replace it.
    • Spike #22 — closed, findings documented in issue context.

    Acceptance Criteria

    7 criteria, all verifiable by an agent:

    • [x] AC1: Copy Link button per row — testable via view rendering
    • [x] AC2: Clipboard copy without navigation — testable via JS behavior
    • [x] AC3: "Copied!" feedback for 2s — testable via JS
    • [x] AC4: Subscription mode with correct price — testable via unit test on StripeClient
    • [x] AC5: Metadata includes player_id, player_name, team — testable via unit test
    • [x] AC6: Pre-fill parent email — testable via unit test
    • [x] AC7: Links stored and reused — testable via integration test

    Minor note: AC1 mentions "players with no parent email show 'No email' indicator" but per schema, players.parent_id is NOT NULL and parents.email is NOT NULL. Every player has a parent with an email. The "No email" edge case may never trigger in practice. Not blocking — the code can handle it defensively, but the agent should be aware.

    Blast Radius

    • PaymentLink model is used by payments_controller (separate concern) — no conflict.
    • Unique constraint uq_payment_links_player_product_tenant on [player_id, product_id, tenant_id] — since product_id is nullable, subscription links can coexist with one-time payment links for the same player. No conflict.
    • Adding a POST method to StripeClient is additive — no existing code affected.
    • payment_links.parent_id is NOT NULL — creating a payment link requires a parent record. This is fine since every player has a parent.
    • No other views or controllers reference clipboard or copy-link functionality.

    Decomposition Assessment

    3 file targets in 1 repo. 7 acceptance criteria (over 5 threshold but tightly coupled). All changes are in a single feature flow: service method + controller wiring + view rendering. Estimated agent work: ~4 minutes. No decomposition needed — this is a cohesive single-pass feature.

    Recommendations

    No blocking recommendations. Minor items for awareness:

    • [SCOPE] Create project page project-westside-ror with user-stories section in pal-e-docs (low priority, does not block this ticket).
    • [SCOPE] Consider creating arch-stripe note in pal-e-docs to back the scope:stripe label (low priority, does not block this ticket).
    • [BODY] Minor: "No email" edge case in AC1 is impossible per current schema (players.parent_id NOT NULL, parents.email NOT NULL). Agent should implement defensively but be aware this path may never trigger. Not worth changing the AC — defensive coding is fine.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- split from #23
    • [x] Repo -- ldraney/westside-ror
    • [x] User Story -- system actor, auto-updates roster
    • [x] Context -- explains gap (no webhook endpoint, DB columns exist)
    • [x] File Targets -- 4 modify/create, 3 do-not-touch
    • [x] Acceptance Criteria -- 8 criteria
    • [x] Test Expectations -- 7 test cases + run command
    • [x] Constraints -- 5 constraints listed
    • [x] Checklist -- standard 3-item
    • [x] Related -- references project, companion tickets, spike, arch doc

    Traceability

    • [x] story:subscription-links label -- present on board item
    • [ ] story note MISSING -- project-westside-ror note does not exist in pal-e-docs (404). [SCOPE] Create project page project-westside-ror with user-stories section including subscription-links story.
    • [x] arch:rails label -- present on board item
    • [ ] arch note MISSING -- no arch-rails note found in pal-e-docs. [SCOPE] Create architecture note arch-rails for the Rails application component. Note: docs/stripe-subscription-architecture.md exists in-repo but is not a pal-e-docs note.
    • [x] Forgejo issue -- ldraney/westside-ror#27, state: open

    File Targets

    • [x] config/routes.rb -- verified: exists, currently has no webhook route (line 1-39). Route addition is straightforward.
    • [x] app/controllers/webhooks_controller.rb -- correctly marked as NEW. Does not exist yet. Will need skip_forgery_protection since ApplicationController inherits from ActionController::Base (no existing CSRF skip patterns in codebase).
    • [x] app/services/stripe_client.rb -- verified: exists (127 lines). Uses raw net/http, no Stripe gem. Adding verify_webhook_signature method here is consistent with existing patterns.
    • [x] Deployment config (env var STRIPE_WEBHOOK_SECRET) -- no existing references. New secret needed.
    • [x] DB columns verified in schema.rb:
      • payment_links.paid_at (line 216) -- datetime, nullable
      • payment_links.stripe_payment_link_id (line 221) -- string, not null
      • players.stripe_subscription_id (line 267) -- string, nullable
      • players.subscription_status (line 268) -- enum (active, past_due, canceled, none), default none

    Repo Placement

    OK. Issue filed on ldraney/westside-ror, all file targets are in that repo. Single-repo change.

    Dependencies

    • #24 (editable fees) -- architecture doc says #24 ships first, but the issue correctly notes this webhook can ship independently. The webhook does not require fee editing to work. No blocking dependency.
    • #26 (copy links UI) -- companion ticket, explicitly independent. No dependency.
    • #22 (spike) -- closed. Produced the architecture doc. No dependency.
    • #23 (original combined ticket) -- still on board in backlog. Should be closed or updated once #26 and #27 replace it. Not a blocker but a housekeeping item.
    • No items in in_progress block this ticket. Items in in_progress (#2 Stripe payment links, #13 email) are unrelated to webhook functionality.

    Acceptance Criteria

    All 8 criteria are testable by an agent. The test expectations map cleanly to acceptance criteria. Run command (rails test) is valid.

    Observations:

    • AC #4 references reading metadata (player_id, team) from checkout.session.completed. The ticket should clarify HOW player_id gets into session metadata -- this is set when creating the payment link (which happens in #26). The webhook needs to handle the case where metadata is missing or incomplete.
    • AC #5 references finding payment_link by stripe_payment_link_id. The checkout session object from Stripe includes a payment_link field -- this is the correct lookup key.
    • Idempotency constraint is stated but not in AC. Could be an explicit test case (processing same event twice should not error).

    Blast Radius

    • subscription_status is read by admin/roster_report_controller.rb (lines 14, 24, 29, 30). Webhook updates to this field will surface automatically in the roster report. No code changes needed there -- this is the desired effect.
    • paid_at is read by StripeClient#db_payment_summary (lines 70, 84), used by the payments dashboard. Again, webhook updates will surface automatically. No blast radius concern.
    • No other controllers or services reference these columns.
    • Adding skip_forgery_protection to the new WebhooksController is scoped -- it only affects that controller, not the app globally.

    Decomposition Assessment

    4 file targets in 1 repo. 8 acceptance criteria. 7 test expectations. Estimated agent work: ~8-10 minutes (new controller, service method, route, tests). This is above the 5-minute threshold but all targets are in one repo and tightly coupled -- decomposing would create artificial seams. The 8 AC are all part of the same webhook handler. A single agent pass is appropriate if the agent is given clear guidance. No decomposition needed.

    Recommendations

    • [SCOPE] Create project page project-westside-ror with user-stories section including the subscription-links story entry.
    • [SCOPE] Create architecture note arch-rails for the Rails application component in pal-e-docs (or re-evaluate whether arch:rails is the right label -- possibly arch:stripe would be more specific).
    • [BODY] Add note to AC #4 about handling missing metadata gracefully (payment links created outside the app may lack player_id metadata).
    • [BODY] Add explicit AC for idempotency: "Processing the same webhook event twice does not create duplicate records or errors."
    • [BODY] Clarify that #23 (the original combined ticket) should be closed now that #26 and #27 replace it.
  • Verdict: NEEDS_REFINEMENT

    Re-review after refinement. 5 of 8 previous findings addressed. Remaining blocker: decomposition still required (14 AC, 6 file targets, 8 points).

    Previous Findings Status

    • [x] [BODY] Fix audit doc reference — FIXED. Now references docs/stripe-subscription-architecture.md in Related section.
    • [x] [BODY] Add dependency on #24 — FIXED. Lineage and Context both document #24 must ship first.
    • [x] [BODY] Add STRIPE_WEBHOOK_SECRET file target — FIXED. Deployment config listed in File Targets.
    • [x] [BODY] Add AC for players without parent email — FIXED. AC 1 covers it, test expectation added, Context section documents behavior.
    • [~] [BODY] Add AC for fee-tier-change edge case — DEFERRED. Not added, but acceptable: #24 owns fee editing and should handle link invalidation when fee changes. Dependency is documented.
    • [ ] [SCOPE] Create project-westside-ror page — Still missing in pal-e-docs. Cannot verify story note.
    • [ ] [SCOPE] Create arch-rails note — Still missing in pal-e-docs. Cannot verify arch note.
    • [ ] [DECOMPOSE] Ticket too large — Still needs decomposition. Issue has grown from 12 to 14 AC.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — follow-up from spike #22, dependency on #24 documented
    • [x] Repo — ldraney/westside-ror
    • [x] User Story — well-formed (As Marcus, I want copy-to-clipboard links)
    • [x] Context — thorough, includes Stripe data, dependency rationale, dollars-vs-cents, no-email handling
    • [x] File Targets — 6 targets listed with rationale, plus 1 do-not-touch
    • [x] Acceptance Criteria — 14 criteria (up from 12)
    • [x] Test Expectations — 8 test cases + run command
    • [x] Constraints — 6 constraints listed (added dollars-vs-cents)
    • [x] Checklist — present
    • [x] Related — lists spike, prerequisite, arch doc, live URL

    All required sections for a Feature template are present and well-structured.

    Traceability

    • [x] story:subscription-links label — present on board item
    • [ ] story note MISSING — project-westside-ror note does not exist in pal-e-docs. Cannot verify user story entry. [SCOPE] Create project page project-westside-ror with user-stories section.
    • [x] arch:rails label — present on board item
    • [ ] arch note MISSING — no arch-rails note found in pal-e-docs. [SCOPE] Create architecture note arch-rails for the Rails component.
    • [x] Forgejo issue — ldraney/westside-ror#23, open

    File Targets

    • [x] config/routes.rb — verified: exists (39 lines), has admin namespace but no webhook route. Correct target.
    • [x] app/controllers/webhooks_controller.rb — verified: does NOT exist yet (new file). Correct.
    • [x] app/services/stripe_client.rb — verified: exists (127 lines), uses raw net/http, has payment_links and checkout_sessions methods but no subscription-mode link creation. Correct target.
    • [x] app/controllers/admin/roster_report_controller.rb — verified: exists (35 lines), loads tenant/teams/players but not payment_links. Correct target.
    • [x] app/views/admin/roster_report/index.html.erb — verified: exists (98 lines) with desktop table and mobile cards. No Copy Link button yet. Correct target.
    • [x] Deployment config for STRIPE_WEBHOOK_SECRET — verified: docker-compose.yml currently only has STRIPE_API_KEY (line 30). Correct target for adding webhook secret.
    • [x] app/controllers/admin/payments_controller.rb — correctly listed as do-NOT-touch.

    Repo Placement

    OK — issue filed on ldraney/westside-ror, all file targets within this repo. Single-repo change.

    Dependencies

    • #24 (editable fees) — prerequisite: Now explicitly documented in Lineage and Context. Architecture doc confirms order: #24 first, then #23. Board item #1334 is also in backlog. Dependency is clear and correctly stated.
    • #1207 (Stripe payment links + email blast, issue #2) — in_progress: Uses same StripeClient and payment_links table for one-time payments. No direct conflict but shared blast radius.
    • CSRF exemption: ApplicationController inherits from ActionController::Base with default CSRF protection. Issue now has AC 13 explicitly requiring CSRF exemption for webhook route. Covered.
    • Schema constraint: payment_links.parent_id is NOT NULL, meaning a PaymentLink record requires a parent. The issue handles this correctly — players without parent email get "No email" indicator instead of a link. Since Player belongs_to :parent (required in Rails 7), all players have a parent, but parent.email may be nil.

    Acceptance Criteria

    14 acceptance criteria — exceeds the 5-AC decomposition threshold by nearly 3x.

    • AC 1-7: Copy-to-clipboard link generation flow (UI + Stripe API + storage + no-email handling)
    • AC 8-13: Webhook endpoint (signature, event handling, status updates, CSRF)
    • AC 14: End-to-end status reflection on roster report

    All criteria are testable and specific. The added ACs (CSRF exemption, no-email handling, status reflection) improve completeness.

    Dollars vs cents: Now documented in Context ("multiply by 100") and Constraints. AC 4 says "correct recurring price tier based on player's monthly_fee" which is clear enough given the constraint.

    Blast Radius

    • StripeClient changes are additive — new methods, existing methods untouched. Safe.
    • payment_links table shared with one-time payment system. New subscription links coexist. The unique constraint uq_payment_links_player_product_tenant means one link per player per product — aligns with AC 7 (reuse on reload).
    • No other controllers handle webhooks — new endpoint is isolated.
    • subscriptionstatus enum already has active, past_due, canceled, none — matches webhook events.
    • paymentlinkstatus enum has active, paid, canceled — sufficient for webhook updates.

    Decomposition Assessment

    NEEDS DECOMPOSITION

    • 14 acceptance criteria (threshold: 5) — up from 12 in first review
    • 6 file targets (threshold: 3) — up from 5
    • 8 test expectations
    • Two distinct subsystems: (a) link generation + copy-to-clipboard UI, (b) webhook endpoint + status sync
    • Estimated agent work: well over 5 minutes — involves Stripe API integration, webhook signature verification, JS clipboard logic, multiple controller/model changes, deployment config
    • 8 story points further confirms this is too large for a single pass

    Natural decomposition split:

    • Sub-ticket A: Subscription link generation + copy-to-clipboard UI (AC 1-7, ~5 points)
    • Sub-ticket B: Webhook endpoint + status sync (AC 8-14, ~3 points)

    Route to skill-decompose-ticket for sub-ticket creation.

    Recommendations

    • [SCOPE] Create project page project-westside-ror with user-stories section including subscription-links story
    • [SCOPE] Create architecture note arch-rails for the Rails component
    • [DECOMPOSE] 14 AC across 2 subsystems (link generation + webhook), 6 file targets, 8 points, 8 test expectations — route to skill-decompose-ticket. Natural split: (a) subscription link generation + copy-to-clipboard UI (AC 1-7), (b) webhook endpoint + status sync (AC 8-14)

    Note: All [BODY] findings from the first review have been addressed. The issue body is now clean and complete. The remaining blocker is decomposition (ticket size) and missing backing notes in pal-e-docs.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — follow-up from spike #22
    • [x] Repo — ldraney/westside-ror
    • [x] User Story — present and well-formed
    • [x] Context — thorough, includes Stripe data audit
    • [x] File Targets — 5 files listed with rationale
    • [x] Acceptance Criteria — 12 criteria
    • [x] Test Expectations — 6 test cases + run command
    • [x] Constraints — 5 constraints listed
    • [x] Open Questions — 3 questions for Lucas/Marcus
    • [x] Checklist — present
    • [x] Related — lists parent spike and upstream PR

    All required sections for a Feature template are present. Issue is thorough and well-structured.

    Traceability

    • [x] story:subscription-links label — present on board item
    • [ ] story note MISSING — project-westside-ror note does not exist in pal-e-docs. Cannot verify user story entry. [SCOPE] Create project page project-westside-ror with user-stories section.
    • [x] arch:rails label — present on board item
    • [ ] arch note MISSING — no arch-rails note found in pal-e-docs. [SCOPE] Create architecture note arch-rails for the Rails component.
    • [x] Forgejo issue — ldraney/westside-ror#23, open

    File Targets

    • [x] config/routes.rb — verified: exists, currently has admin namespace and email routes but no webhook route. Correct target for adding post '/webhooks/stripe'.
    • [x] app/controllers/webhooks_controller.rb — verified: does NOT exist yet (new file). Correct — needs creation.
    • [x] app/services/stripe_client.rb — verified: exists at 127 lines, uses raw net/http pattern, has payment_links and checkout_sessions methods but no subscription-mode link creation. Correct target.
    • [x] app/controllers/admin/roster_report_controller.rb — verified: exists at 35 lines, loads tenant/teams/players. Currently does not load payment_links. Correct target.
    • [x] app/views/admin/roster_report/index.html.erb — verified: exists at 98 lines with desktop table and mobile card layout. No "Copy Link" button yet. Correct target.
    • [x] app/controllers/admin/payments_controller.rb — verified: exists, separate one-time payment flow. Correctly listed as "do NOT touch".

    Issue: Context section references docs/reports/2026-06-01-stripe-payment-link-audit.md but this file does not exist. The actual spike output is at docs/stripe-subscription-architecture.md.

    Repo Placement

    OK — issue is filed on ldraney/westside-ror and all file targets are within this repo. Single-repo change.

    Dependencies

    • Ordering conflict: The architecture doc (docs/stripe-subscription-architecture.md, line 172-173) specifies implementation order as #24 first, then #23. However, #23 is being reviewed for advancement while #24 (inline-editable monthly fee with Stripe sync) is also in backlog. If #24 lands first, it changes how monthly_fee works and could affect which Stripe price tier the subscription link uses. The issue should explicitly document whether it depends on #24 or can proceed independently.
    • Board item #1334 (Inline-editable monthly fee, Forgejo #24) — same story:subscription-links label, also in backlog. These two tickets share the same Stripe product and price tiers. No documented dependency between them on the board.
    • Board item #1207 (Stripe payment links + email blast, Forgejo #2) — in_progress. Uses the same StripeClient and payment_links table but for one-time payments. No conflict but blast radius overlap.
    • CSRF exemption needed: ApplicationController inherits from ActionController::Base which has CSRF protection enabled by default. The webhook controller must skip this. Issue mentions this in Constraints but not in File Targets or AC.
    • Webhook signing secret: Issue says "must go through secrets management" but does not specify the env var name or where it will be configured (docker-compose.yml currently only has STRIPE_API_KEY). File target for docker-compose.yml or deployment config is missing.

    Acceptance Criteria

    12 acceptance criteria — exceeds the 5-AC decomposition threshold.

    • AC 1-7 cover the copy-to-clipboard link generation flow (UI + Stripe API + storage)
    • AC 8-12 cover the webhook endpoint (signature verification, event handling, status updates)
    • These are two distinct subsystems that could be implemented and tested independently.

    Schema concern: AC 9 says webhook "sets paid_at" — this column is on payment_links table (not players), which is correct but the AC text is ambiguous about which record.

    Unique constraint concern: payment_links has a unique constraint on (player_id, product_id, tenant_id). If all subscription links use the same "Monthly Club Dues" product, a player can only have one link. The ticket says "reused on subsequent page loads" (AC 7) which aligns. But if a player's fee tier changes, the old link must be deactivated and a new one created — this edge case is not covered in the AC.

    Missing AC: No acceptance criterion for players without a parent record (Open Question #3 acknowledges this but doesn't resolve it).

    Blast Radius

    • StripeClient is used by both Admin::PaymentsController (one-time payments) and the roster report. Adding subscription link methods is additive and should not break existing one-time flows.
    • payment_links table is shared with the one-time payment system. New subscription links will coexist. The paymentlinkstatus enum already includes active, paid, canceled values.
    • No other controllers currently handle webhooks, so the new endpoint is isolated.
    • The subscriptionstatus enum already has active, past_due, canceled, none — matches webhook events perfectly.

    Decomposition Assessment

    NEEDS DECOMPOSITION

    • 12 acceptance criteria (threshold: 5)
    • 5 file targets (threshold: 3)
    • Two distinct subsystems: (a) link generation + copy UI, (b) webhook endpoint + status updates
    • Estimated agent work: well over 5 minutes — involves Stripe API integration, webhook signature verification, JS clipboard logic, and multiple controller/model changes
    • 8 story points further confirms this is too large for a single pass

    Route to skill-decompose-ticket for sub-ticket creation.

    Recommendations

    • [BODY] Fix audit doc reference: docs/reports/2026-06-01-stripe-payment-link-audit.mddocs/stripe-subscription-architecture.md
    • [BODY] Add dependency note: document whether #23 depends on #24 or can proceed independently (architecture doc says #24 first)
    • [BODY] Add file target: docker-compose.yml or deployment config for STRIPE_WEBHOOK_SECRET env var
    • [BODY] Add AC for fee-tier-change edge case: when monthly_fee changes, old link must be deactivated and new link created
    • [BODY] Add AC for players without a parent record (resolve Open Question #3)
    • [SCOPE] Create project page project-westside-ror with user-stories section including subscription-links story
    • [SCOPE] Create architecture note arch-rails for the Rails component
    • [DECOMPOSE] 12 AC across 2 subsystems (link generation + webhook), 5 file targets, 8 points — route to skill-decompose-ticket. Natural split: (a) subscription link generation + copy-to-clipboard UI (AC 1-7), (b) webhook endpoint + status sync (AC 8-12)
Board 1