pal-enterprises
Notes
Architecture 2
-
Architecture: Rails App (Shared Pattern)
arch-rails-appArchitecture: Rails App (Shared Pattern)
Common deployment architecture for Rails applications on the pal-e platform. Currently implemented by
landscaping-assistant(production) andpalinks(in development). New Rails apps SHOULD follow this pattern unless there is a documented reason to diverge.Diagram
graph TB subgraph "Developer Machine" DEV[Local dev serverbin/dev] end subgraph "Forgejo" REPO[App repoldraney/app-name] WP[Woodpecker CI] end subgraph "Harbor Registry" IMG[harbor.tail5b443a.ts.netproject/app-name:sha] end subgraph "k3s Cluster -- Home Lab" ARGO[ArgoCD] subgraph "App Namespace" POD[Rails Pod] SVC[ClusterIP Service] MON[ServiceMonitor] end subgraph "Shared Services" CNPG[CNPG PostgreSQL 17] KC[Keycloak OIDC] MINIO[MinIO S3] PROM[Prometheus] GRAF[Grafana] LOKI[Loki + Promtail] end end subgraph "Hetzner Edge" CADDY[Caddy reverse proxy178.156.129.142] end subgraph "GoDaddy" DNS[A record -> edge IP] end subgraph "Internet" USER[End user] end DEV -->|git push| REPO REPO -->|webhook| WP WP -->|test + build + push| IMG IMG -->|Image Updater| ARGO ARGO -->|kustomize apply| POD POD --> CNPG POD -.->|where integrated| KC POD -.->|where needed| MINIO MON --> PROM PROM --> GRAF POD -->|lograge JSON| LOKI SVC -->|Tailscale mesh| CADDY DNS --> CADDY USER -->|HTTPS| CADDYComponents
Component Purpose Notes Rails Pod Application server Rails 8.1, Hotwire (Turbo + Stimulus). Monolithic -- no separate frontend, Rails serves HTML directly. Importmap for JS, no build pipeline. CNPG PostgreSQL 17 Primary database CloudNativePG operator on k3s. Shared cluster, per-app databases. Handles failover, backups, WAL archiving. Keycloak OIDC Authentication (opt-in) Authorization Code + PKCE via OmniAuth. OAuth 2.1 compliant. NOT all apps have this -- landscaping-assistant: yes, palinks: planned. MinIO S3 Object storage (opt-in) ActiveStorage with S3-compatible backend. Local disk in dev. Only for apps with file/image uploads -- landscaping-assistant: yes, palinks: no. Woodpecker CI CI pipeline Triggered by Forgejo webhook. Stages: test (rspec + postgres service), build (multi-stage Docker), push to Harbor. Harbor Registry Container image store harbor.tail5b443a.ts.net. Images tagged with full commit SHA. Trivy vulnerability scanning. ArgoCD + Image Updater Continuous deployment Image Updater detects new SHA tags, writes to kustomize overlay in pal-e-deployments. ArgoCD syncs to cluster. ServiceMonitor Metrics scraping Prometheus scrapes /metrics endpoint exposed by yabeda-rails + yabeda-prometheus. Grafana + Loki Observability Grafana dashboards for metrics. Loki + Promtail for log aggregation. Structured JSON logs via lograge. Caddy (Hetzner edge) Public reverse proxy + TLS 178.156.129.142, CPX11 Ashburn. Auto-TLS for custom domains. Connected to home lab via Tailscale mesh (tail5b443a.ts.net). GoDaddy DNS Domain management A records pointing custom domains to Hetzner edge IP. Managed by godaddy-tofu provider via pal-e-platform/terraform/. Kustomize overlays Deployment manifests Live in pal-e-deployments repo. Follow convention-kustomize-overlay pattern. Image tags updated by ArgoCD Image Updater. Deployment Pipeline
- Push to Forgejo -- triggers Woodpecker CI webhook
- CI: Test --
bundle exec rspecagainst PostgreSQL service container (version 17, matching prod) - CI: Build -- multi-stage Docker build
- CI: Push -- push image to Harbor (
harbor.tail5b443a.ts.net/project/app-name:<commit-sha>) - ArgoCD Image Updater -- detects new tag (regexp
^[0-9a-f]{7,40}$), writes SHA to kustomize overlay - ArgoCD Sync -- applies updated kustomization to k3s cluster
- Auto-migration --
rails db:migrateruns on deploy
Public Domain Routing
graph LR GD[GoDaddy DNSA record] -->|domain.app -> 178.156.129.142| HZ[Hetzner EdgeCaddy + auto-TLS] HZ -->|Tailscale mesh| K3S[k3s ServiceClusterIP] K3S --> POD[Rails Pod]Three steps to a public domain: (1) godaddy-tofu provider creates A record pointing to 178.156.129.142, (2) Caddy on Hetzner edge terminates TLS and reverse-proxies via Tailscale, (3) k3s ClusterIP service routes to Rails pod.
Variations by App
Concern landscaping-assistant palinks Auth Keycloak (Auth Code + PKCE, OmniAuth). Five-role model. Session-based. Planned, not yet integrated. Object storage ActiveStorage + MinIO for photo uploads. Not needed (link app, no file uploads). Mobile Turbo Native iOS build. Dev funnel at landscaping-dev.tail5b443a.ts.net. Web-only. Observability depth Full Grafana ecosystem: 26 dashboards, golden signals, blackbox probes, DORA metrics. ServiceMonitor deployed, metrics parity achieved. Domain-specific services Nominatim geocoding. None. Test suite 230+ RSpec specs (model, request, system). Capybara + Cuprite. In development. Custom domain landscaping-assistant.app (when configured). palinks.app (DNS pending godaddy-tofu integration). Key Decisions
- Monolithic Rails over microservices -- single deployable unit per app. No API gateway complexity. Frontend and backend in one repo, one process.
- Hotwire over SPA -- server-rendered HTML with progressive enhancement via Turbo Frames/Streams. No JS build pipeline (importmap). Turbo Native wraps the same app for mobile.
- CNPG shared cluster -- one CloudNativePG operator instance, separate databases per app. Operator manages failover, backups, WAL archiving. CI postgres version pinned to match prod (17).
- Keycloak opt-in, not mandatory -- apps that need auth use Authorization Code + PKCE (OAuth 2.1 compliant). ROPC/Direct Access Grants rejected as deprecated (RFC 9700). Apps without auth needs skip Keycloak entirely.
- MinIO opt-in -- only apps with file/image storage use ActiveStorage + MinIO. S3-compatible API provides portability. Dev uses local disk.
- Hetzner edge for public ingress -- Caddy handles TLS termination on a public IP. Tailscale mesh avoids exposing the home lab directly. Single edge node at 178.156.129.142 serves all custom domains.
- Commit-SHA image tags -- immutable, traceable. ArgoCD Image Updater automates tag detection and kustomize overlay updates.
- RSpec + Capybara + Cuprite for testing -- system specs run headless Chromium via CDP. No Selenium dependency.
- Structured logging via lograge -- JSON output collected by Promtail into Loki. No unstructured Rails default logs in production.
Related
project-palinks-- palinks project pageproject-landscaping-assistant-- landscaping-assistant project pagearch-godaddy-tofu-- DNS provider architecture (manages A records for custom domains)convention-kustomize-overlay-- deployment overlay pattern used by pal-e-deploymentsconvention-pipeline-stages-- CI pipeline stage conventionconvention-frontend-css-- CSS conventions for frontend work
-
Architecture: pal-enterprises Rails App
arch-rails-app-pal-enterprisesArchitecture: pal-enterprises Rails App
Single Rails 8.1 app serving the platform front door.
Components
- PagesController -- public landing page at
/ - ContactsController -- stateless contact form, delivers via Action Mailer
- SessionsController -- Keycloak OIDC login/logout/callback
- DashboardController -- authenticated tool grid, session-gated
Auth Flow
sequenceDiagram participant V as Visitor participant R as Rails App participant K as Keycloak (pal-e realm) V->>R: GET / R-->>V: Landing page (public) V->>R: Click "Sign In" R->>K: Redirect to /auth (OIDC) K-->>V: Login form V->>K: Credentials K->>R: Callback with auth code R->>K: Exchange code for tokens K-->>R: ID token + access token R-->>V: Redirect to /dashboard (session set)Deployment
graph TB TS[Tailscale Ingress] --> SVC[k8s Service :3000] SVC --> POD[Rails Pod - ruby:3.4-slim] POD --> |hostPath| SRC[~/pal-enterprises] POD --> |PVC| GEMS[gem-cache] POD --> |tcp:5432| PG[CNPG pal-e-postgres-rw] POD --> |https| KC[Keycloak]Key Decisions
- No local user table -- Keycloak sub/name/email stored in session only
- Contact form is stateless -- email delivery, no DB persistence
- omniauth-openid-connect gem for OIDC
- Tailwind CSS, Hotwire (Turbo + Stimulus)
Related
project-pal-enterprises-- project pagesop-keycloak-client-creation-- SOP for registering the OIDC clientarch-deployment-pal-enterprises-- deployment architecture (TBD)
- PagesController -- public landing page at
Doc 11
-
Validation: T5 Woodpecker CI with test gates (2026-05-10)
validation-19-2026-05-10Validation: T5 — Woodpecker CI with test gates
Issue: ldraney/pal-enterprises#19
Date: 2026-05-10
Verdict: PASS
Evidence
- Pipeline #26 (main branch): all 4 steps green — clone, test, build-and-push, update-kustomize-tag
- Test gates validated: bundle-audit (0 CVEs), brakeman (0 warnings), rubocop (0 offenses)
- Docker image pushed to harbor.tail5b443a.ts.net/pal-enterprises/app:d8900c1e
- Kustomize tag updated in pal-e-deployments, ArgoCD picks up new image
Issues resolved during validation
- Woodpecker agents cannot pull from Harbor cluster-internal URL — switched to external Tailscale URL
- Harbor pal-e project made public for unauthenticated base image pulls
- harbor-portal-proxy missing client_max_body_size (413 on push) — fixed ConfigMap
- omniauth 1.9.2 → 2.1.4 (CVE-2015-9284)
- Kaniko layer caching added for faster subsequent builds
PRs merged
- #22: Add Woodpecker CI pipeline with test gates
- #24: Fix CI pipeline: use Docker Hub image + fix security audit
- #25: Fix kaniko insecure pull (interim, superseded by #26)
- #26: Switch kaniko to external Harbor URL
- #27: Add kaniko layer caching
-
Validation: T4 Dockerfile Migration to Arch Base
validation-18-2026-05-10Ticket
pal-enterprises#18 — Migrate Dockerfile from Debian ruby:slim to Arch Linux base image. PRs: #20 (migration), #21 (tag fix).
Environment
Local Docker build with external Harbor URL substitution. Image:
pal-enterprises:arch-test.Checks
# Criterion How to Verify Result Evidence 1 Dockerfile builds successfully docker buildwith Arch basePASS Build completed, all stages successful including asset precompilation 2 Ruby version docker run ... ruby --versionPASS ruby 3.4.8 (2025-12-17) +PRISM [x86_64-linux] 3 Rails boots rails runner "puts 'ok'"PASS Rails runner works (no DB needed for boot test) 4 jemalloc preloaded Check LD_PRELOAD and lib presence PASS LD_PRELOAD=/usr/lib/libjemalloc.so, file present 5 Non-root user whoamiin containerPASS USER=rails 6 No Debian artifacts which apt-getPASS apt-get not found 7 .ruby-version removed File check PASS Deleted in PR #20 8 k8s/dev.yaml removed File check PASS Deleted in PR #20 Verdict
PASS — Dockerfile builds on Arch base, Rails boots, jemalloc loaded, non-root user, no Debian remnants. Full /up health check deferred to post-deploy (requires Postgres).
Discovered Issues
- Missing :build tag: The ruby-arch pipeline only pushes
:latest(which is the build stage). The Dockerfile originally referenced:buildwhich doesn't exist. Fixed in PR #21 by using:latestfor both stages. Follow-up: update ruby-arch pipeline to push separate:baseand:buildtags via Kaniko--target.
- Missing :build tag: The ruby-arch pipeline only pushes
-
Validation: Fix gem bin PATH in ruby-arch base image
validation-363-2026-05-10Ticket
pal-e-platform#363 — Fix gem bin PATH in ruby-arch base image.
Environment
Rebuilt image via Woodpecker pipeline 554 (manual trigger). Image:
harbor.tail5b443a.ts.net/pal-e/ruby-arch:latest.Checks
# Criterion How to Verify Result Evidence 1 bundler in PATH docker run ... bundler --versionPASS 4.0.11, located at /usr/sbin/bundler 2 bundle in PATH docker run ... bundle --versionPASS 4.0.11, located at /usr/sbin/bundle Verdict
PASS
-
Validation: T3 Arch Linux Ruby Base Image
validation-360-2026-05-10Ticket
pal-e-platform#360 — Create shared Arch Linux Ruby base image in Harbor with weekly rebuild pipeline.
Environment
Harbor project
pal-e(id=42). Woodpecker pipeline 547 (manual trigger on main). Image:harbor.tail5b443a.ts.net/pal-e/ruby-arch:latest.Post-merge infra steps performed
- Created
pal-eHarbor project (HTTP 201) - Created Harbor robot
robot$pal-e+pal-e-ciwith push/pull permissions - Added
harbor_usernameandharbor_passwordrepo secrets (manual+cron events) - Moved pipelines to
.woodpecker/for multi-pipeline discovery (PR #362, merged) - Updated all 25 repo secrets to allow manual+cron events (Woodpecker validates secrets across all discovered pipelines)
- Created Woodpecker cron
ruby-arch-weekly-rebuild(Sunday 6am UTC)
Checks
# Criterion How to Verify Result Evidence 1 Harbor project exists Harbor API query PASS pal-eproject created, id=422 Pipeline builds and pushes image Woodpecker pipeline 547 manual trigger PASS All steps success: clone, build-and-push 3 Image exists in Harbor Harbor API: /projects/pal-e/repositoriesPASS pal-e/ruby-arch:latest, 366MB, 1 artifact4 Ruby works docker run ... ruby --versionPASS ruby 3.4.8 (2025-12-17) 5 Bundler installed docker run ... gem list bundlerPASS bundler 4.0.11 installed; executable at /root/.local/share/gem/ruby/3.4.0/bin/bundler6 jemalloc preloaded docker run ... ruby -e "puts :ok"(LD_PRELOAD set in image ENV)PASS Runs without error, jemalloc loaded 7 Weekly cron configured Woodpecker cron list PASS ruby-arch-weekly-rebuild, schedule0 6 * * 0, branch mainVerdict
PASS — image builds, pushes, and runs correctly. All components verified: Ruby 3.4.8, Bundler 4.0.11, jemalloc preload, weekly cron.
Discovered Issues
- Gem bin not in PATH: Arch's Ruby puts
gem installbinaries in/root/.local/share/gem/ruby/3.4.0/bin/rather than/usr/bin/. Thebundlercommand isn't in PATH by default. Fix: addENV PATH="/root/.local/share/gem/ruby/3.4.0/bin:${PATH}"to the Dockerfile, or usegem install --no-user-install bundler. Minor — downstream Dockerfiles can work around it. - Multi-pipeline secret validation: Woodpecker validates secrets from ALL discovered
.woodpecker/*.yamlfiles against the event type, even for pipelines whosewhenconditions exclude that event. Required updating all 25 existing secrets to allow manual+cron events. This is a Woodpecker quirk worth documenting.
- Created
-
Validation: T1 Keycloak Dev Redirect + ArgoCD App
validation-75-2026-05-10Ticket
pal-e-services#75 — Add dev redirect URI to Keycloak pal-enterprises client and create ArgoCD Application resource.
Environment
Prod cluster via
tofu apply -var-file=k3s.tfvarsin~/pal-e-services/terraform. Keycloak realm:pal-enterprises. ArgoCD namespace:argocd.Checks
# Criterion How to Verify Result Evidence 1 Keycloak client has dev redirect URI Keycloak Admin API query for pal-enterprises client redirectUris PASS Both URIs present: pal-enterprises.tail5b443a.ts.net/auth/keycloak/callbackandpal-enterprises-dev.tail5b443a.ts.net/auth/keycloak/callback2 Keycloak client has dev web origin Keycloak Admin API query for webOrigins PASS Both origins present: prod and pal-enterprises-dev.tail5b443a.ts.net3 ArgoCD Application created and synced kubectl get application -n argocd pal-enterprisesPASS Status: Synced/Progressing (Progressing expected — no image built yet) 4 Prod app unaffected curl -sf https://pal-enterprises.tail5b443a.ts.net/upPASS Returns 200 with green health check page. Existing pod still Running. 5 tofu plan shows no drift tofu plan -var-file=k3s.tfvarsafter applyPASS Apply completed: 1 added, 14 changed, 0 destroyed. All T1-scoped resources reconciled. Verdict
PASS — all checks green. Keycloak client correctly configured with both prod and dev redirect URIs. ArgoCD app created and synced. Prod unaffected. ImagePullBackOff on new pod is expected — no CI-built image exists yet (separate ticket scope).
Discovered Issues
None. The 13 harbor-creds label drift changes were pre-existing ArgoCD label reconciliation, not introduced by T1.
-
Architecture: Harbor
arch-harborArchitecture: Harbor
Harbor is the private container registry for the pal-e platform, deployed via Helm in
pal-e-platform/terraform/modules/harbor/.Image Strategy
Shared Arch Linux base images in the
pal-eHarbor project, consumed by per-app image builds:pal-e/ruby-arch:latest— Arch + Ruby + bundler + jemalloc + postgresql-libs (weekly rebuild)pal-enterprises/app:{SHA}— app image, FROM ruby-arch base
Registry URLs
- Internal (CI):
harbor.harbor.svc.cluster.local - External:
harbor.tail5b443a.ts.net
Weekly Base Image Rebuild
A Woodpecker cron pipeline in
pal-e-platformrebuilds the Arch base image weekly. Arch's rolling release means each rebuild picks up the latest Ruby, system libs, and security patches automatically — eliminating manual version bump tickets. -
Architecture: Keycloak
arch-keycloakArchitecture: Keycloak
Keycloak provides OIDC authentication for pal-enterprises. Single realm (
pal-enterprises), single client (pal-enterprises), non-public with PKCE.Client Pattern
Single client with multiple redirect URIs for dev/prod separation (same pattern as westside-admin, playme2k):
- Prod:
https://pal-enterprises.tail5b443a.ts.net/auth/keycloak/callback - Dev:
https://pal-enterprises-dev.tail5b443a.ts.net/auth/keycloak/callback
Config Location
- Client definition:
pal-e-services/terraform/k3s.tfvars(lines 238-257) - App config:
pal-enterprises/config/initializers/omniauth.rb - Managed via Terraform (IaC), not Keycloak admin console
Future: Client Board Access
Per
arch-multi-tenant, client users will get aproject_sluguser attribute mapped via OIDC protocol mapper. This enables scoped board access through the pal-enterprises proxy pattern. - Prod:
-
Story: Infrastructure Overhaul
story-infra-overhaulStory: Infrastructure Overhaul
Migrate pal-enterprises from Debian-based Ruby images to Arch Linux rolling-release base images, establish proper dev/prod deployment separation, and add CI quality gates.
Motivation
Every Ruby version bump is a manual ticket. Dev environment points to prod URL with hardcoded secrets. No CI test gates. ArgoCD app not wired up. This story fixes the entire pipeline from image → build → deploy → dev.
Tickets
Wave Ticket Repo Pts Issue 1 T1: Keycloak dev redirect + ArgoCD pal-e-services 3 #75 1 T3: Arch Ruby base image in Harbor pal-e-platform 5 #360 2 T2: Dev overlay + local mount pal-e-deployments 3 #158 2 T4: Dockerfile migration to Arch pal-enterprises 3 #18 3 T5: CI pipeline with test gates pal-enterprises 3 #19 Outcome
- Weekly Arch base image rebuild → no manual Ruby/gem version tickets
- Prod via ArgoCD, dev via local mount at pal-enterprises-dev.tail5b443a.ts.net
- CI runs brakeman + rubocop + bundle-audit on every PR
- Keycloak accepts both prod and dev OAuth callbacks
Tickets Killed
#14(Ruby 4.0 upgrade) — unnecessary, Arch rolling release handles this- Future Ruby/gem version bump tickets — eliminated as a class of work
-
Validation: Kustomize overlay pal-enterprises prod (#156)
validation-156-2026-05-10Ticket
ldraney/pal-e-deployments#156 — Kustomize overlay: overlays/pal-enterprises/prod/
Shipped: PR #157 merged (
f980c45).Environment
Prod cluster. Namespace:
pal-enterprises.Checks
# Criterion How to Verify Result Evidence 1 Overlay directory exists ls overlays/pal-enterprises/prod/PASS Contains kustomization.yaml + deployment-patch.yaml 2 Image set correctly Inspect kustomization.yaml PASS harbor.tail5b443a.ts.net/pal-enterprises/app:latest3 Port consistency Check containerPort, service targetPort, probes PASS All port 3000 4 Secrets by reference Grep for inline values PASS All via pal-enterprises-secretssecretRef5 App running kubectl get pods -n pal-enterprisesPASS 1/1 Ready, deployment Available 6 Health check /upendpointPASS Returns 200 Discovered Issues
Minor: in-cluster service-to-service calls via
pal-enterprises:3000return 403 due to Rails Host Authorization. Not blocking — only affects hypothetical internal service consumers. Fix:config.hosts << "pal-enterprises"if needed later.Verdict
PASS — all checks green. Ready for done.
-
Validation: NetworkPolicy pal-enterprises → Postgres + Keycloak (#357)
validation-357-2026-05-10Ticket
ldraney/pal-e-platform#357 — NetworkPolicy: allow pal-enterprises → Postgres + Keycloak
Shipped: PR #359 merged (
b2aea40),tofu applycompleted.Environment
Prod cluster. Namespaces:
pal-enterprises,postgres,keycloak.Checks
# Criterion How to Verify Result Evidence 1 NetworkPolicy allows pal-enterprises → Postgres kubectl get networkpolicy -n postgres -o yamlPASS pal-enterprises namespace in ingress allowlist (line 152 of network-policies.tf) 2 NetworkPolicy allows pal-enterprises → Keycloak kubectl get networkpolicy -n keycloak -o yamlPASS pal-enterprises namespace in ingress allowlist (line 176 of network-policies.tf) 3 DB connectivity works rails runner 'puts ActiveRecord::Base.connection.execute("SELECT 1").to_a'PASS Returned {"test" => 1}4 No connection errors in logs kubectl logs -n pal-enterprises deployment/pal-enterprisesgrep for error/fail/refusePASS Zero matches Verdict
PASS — all checks green. Ready for done.
-
Architecture: Multi-Tenant Access
arch-multi-tenantArchitecture: Multi-Tenant Client Board Access
Decision
Option B — pal-enterprises proxy pattern. Clients access their project board through pal-enterprises, which fetches data from pal-e-docs internally and renders a read-only view. pal-e-docs remains an internal ops tool with no client-facing auth.
Decided: 2026-05-10. Spike: ldraney/pal-enterprises#13
Options Evaluated
Option A — pal-e-docs native scoping (rejected): Would require adding multi-tenant RBAC to pal-e-docs. The API has zero auth —
list_boards()returns all boards,list_board_items()has no scoping. Changes the fundamental nature of pal-e-docs from internal ops tool to client-facing platform. High effort, high risk, wrong separation of concerns.Option B — pal-enterprises proxy (selected): pal-enterprises already owns client auth (Keycloak OIDC). A new controller fetches board data via pal-e-docs API (internal, service-to-service), renders read-only view filtered to client's project. ~100 lines of Rails code, no changes to pal-e-docs. Lucas controls what clients see — can filter labels, hide ops-only items.
Option C — Hybrid (not needed): No advantage over pure proxy since pal-e-docs doesn't need client auth for any other reason.
Keycloak Mapping Strategy
- Add
project_sluguser attribute to client users in Keycloak (e.g.project_slug: "westside") - Configure a protocol mapper on the pal-enterprises OIDC client to include
project_slugin token claims - pal-enterprises reads the claim, fetches
list_board_items(board_slug: "board-{project_slug}") - Same attribute can gate Forgejo repo access later via Keycloak group/role mapping
Implementation Shape
- Keycloak: Add
project_sluguser attribute + OIDC protocol mapper - pal-enterprises:
ClientBoardController— reads claim, calls pal-e-docs API, renders read-only view - pal-enterprises: Board view template using existing CSS design system (columns, cards, status indicators)
- No write endpoints — pure display, no forms, no mutations
Follow-up ticket: ldraney/pal-enterprises#17
Read-Only Enforcement
- pal-enterprises only exposes GET routes for the board view
- pal-e-docs API calls are server-side only — client browser never contacts pal-e-docs
- No client-side JS that could be exploited to make write calls
Related
ldraney/pal-enterprises#13— spike that produced this decisionldraney/pal-enterprises#17— follow-up feature ticketldraney/pal-enterprises#9— owner dashboard (Lucas sees all clients)
- Add
Review 14
-
Review: T3: Arch Linux Ruby base image in Harbor (r2)
review-1201-2026-05-10-r2Verdict: READY
Template Completeness
- [x] Type -- present (Infra)
- [x] Lineage -- present (Plan: infra overhaul, Ticket 3 of 5)
- [x] Repo -- present (ldraney/pal-e-platform)
- [x] User Story -- present
- [x] Context -- present, thorough
- [x] File Targets -- present (2 creates, 1 conditional modify)
- [x] Dockerfile Shape -- present with verified package names
- [x] Pipeline Shape -- present
- [x] Acceptance Criteria -- present (11 items)
- [x] Test Expectations -- present (4 items)
- [x] Dependencies -- present (added since r1: T2 and T4 as downstream dependents)
- [x] Constraints -- present
- [x] Checklist -- present (added since r1: PR opened, Dockerfile builds, image pushed, cron configured, no unrelated changes)
- [x] Related -- present with cross-repo issue references
All template sections are complete. Every item flagged as missing in review-1201-2026-05-10 has been addressed.
Traceability
- [x] story:infra-overhaul label -- present on board item
- [x] story note verified --
story-infra-overhaulnote exists in pal-e-docs (created since r1). Has Motivation, Tickets, Outcome, and Tickets Killed sections. - [x] arch:harbor label -- present on board item
- [x] arch note verified --
arch-harbornote exists in pal-e-docs (created since r1). Has Image Strategy, Registry URLs, and Weekly Base Image Rebuild sections. - [x] Forgejo issue -- ldraney/pal-e-platform#360, state: open
Minor gap (non-blocking): The
project-pal-enterprisesuser-stories table does not yet have aninfra-overhaulrow. Current entries: landing-page, sso-gateway, tool-dashboard, client-portal. The story note exists as a standalone note, which is sufficient for traceability. Adding the row to the project page is a housekeeping task, not a scope blocker.File Targets
- [x]
docker/ruby-arch/Dockerfile-- to be CREATED. Directory does not exist yet (expected). Dockerfile shape provided inline with verified Arch package names (ruby, jemalloc, postgresql-libs, libyaml, base-devel, git, pkgconf). Package corrections documented: ruby-bundler replaced with gem install, libpq removed, pkg-config corrected to pkgconf. - [x]
docker/ruby-arch/.woodpecker.yaml-- to be CREATED. Pipeline shape described inline. Note: Woodpecker does not auto-discover yaml files in subdirectories. The existing pal-e-platform uses a single root.woodpecker.yaml. The agent will need to either (a) place this as a separate pipeline config in a.woodpecker/directory at repo root, or (b) add steps to the root pipeline with path-based triggers. The issue body describes the intent clearly enough for an agent to resolve this at implementation time. - [x]
terraform/modules/harbor/main.tf-- VERIFIED EXISTS (575 lines). Contains Harbor Helm release, OIDC config, portal CSS proxy. No Harbor project resource exists yet. The issue correctly identifies this as a conditional modify to add thepal-eHarbor project.
Repo Placement
OK. Issue filed on ldraney/pal-e-platform. All file targets (docker/, terraform/) are pal-e-platform paths. Board item is on board-pal-enterprises which is correct since this infra supports the pal-enterprises app. No cross-repo mismatch.
Dependencies
Dependencies now documented in the issue body (fixed since r1):
- Upstream: None -- T3 can start immediately. Wave 1 parallel with T1.
- Downstream: T2 (pal-e-deployments#158, dev overlay) and T4 (pal-enterprises#18, Dockerfile migration) both depend on this base image existing in Harbor.
Board state confirms: T1 (#1200), T2 (#1197), T3 (#1201), T4 (#1198), T5 (#1199) are all in backlog. No blocking items in in_progress.
Acceptance Criteria
11 ACs are specific and testable. Key improvements since r1:
- Harbor project creation AC added (was missing)
- Kaniko + pacman compatibility AC added (was missing)
- Manual pipeline trigger AC added
All criteria are verifiable by an agent post-implementation. Test commands are concrete (docker run, gem install pg, LD_PRELOAD verification).
Blast Radius
Low. Creates new files only. The existing pal-enterprises Dockerfile (ruby:3.4.8-slim Debian) is untouched. No existing pipelines are modified. Future consumers (pal-enterprises, westside-ror, pal-e-ror, westside-docs) will be migrated by T4 separately. The existing pal-enterprises .woodpecker.yaml uses Kaniko (plugin-kaniko:2.3.0) for app builds -- this pipeline would add a new, separate build pipeline for the base image.
Decomposition Assessment
3 file targets in 1 repo. 11 ACs + 4 test expectations exceeds the >5 AC threshold numerically, but the work is cohesive: write one Dockerfile, write one pipeline config, optionally add one Terraform resource. All three files serve a single purpose (build and push a base image). Estimated agent work is within 5 minutes -- Dockerfile and pipeline shapes are provided inline. No decomposition needed.
Refinement Fixes Verified (from r1)
- [x] Dockerfile package names corrected (ruby-bundler to gem install, libpq removed, pkg-config to pkgconf)
- [x] Dependencies section added (T2 and T4 as downstream dependents)
- [x] Missing ACs added (Harbor project creation, Kaniko+pacman compatibility)
- [x]
story-infra-overhaulnote created - [x]
arch-harbornote created - [x] Checklist section added
Recommendations
No action needed. Ticket is ready for implementation.
Housekeeping (non-blocking): Add
infra-overhaulrow toproject-pal-enterprisesuser-stories table for completeness. -
Review: T1: Keycloak dev redirect + ArgoCD app (R2)
review-1200-2026-05-10-r2Verdict: APPROVED
Re-review after refinement. All four NEEDS_REFINEMENT items from
review-1200-2026-05-10have been addressed. One minor project-page housekeeping item remains but does not block the ticket.Template Completeness
- [x] Type -- "Infra" (maps to Task base template)
- [x] Lineage -- "Plan: pal-enterprises infrastructure overhaul (Ticket 1 of 5). Story: story-infra-overhaul."
- [x] Repo -- ldraney/pal-e-services
- [x] User Story -- developer dev-environment auth flow
- [x] Context -- clear motivation, references arch-keycloak note
- [x] File Targets -- terraform/k3s.tfvars with line references
- [x] Acceptance Criteria -- 5 items, all concrete
- [x] Test Expectations -- 2 items, verifiable
- [x] Constraints -- 3 items, clear
- [x] Checklist -- FIXED: now present with infra-appropriate items (tofu plan reviewed, tofu apply succeeds)
- [x] Dependencies -- added: "None -- can start immediately. Wave 1 parallel with T3."
- [x] Related -- FIXED: broken sop-keycloak-client-creation removed, real references added (story-infra-overhaul, arch-keycloak, pal-e-deployments#156, pal-e-platform#360, pal-e-deployments#158)
Traceability
- [x] story:infra-overhaul label -- present on board item
- [x] story note verified --
story-infra-overhaulnote exists in pal-e-docs (id: 1727), tags: active, story. Contains ticket table with all 5 tickets, wave assignments, and outcomes. - [ ] story entry on project page -- infra-overhaul is NOT listed in project-pal-enterprises user-stories table (only landing-page, sso-gateway, tool-dashboard, client-portal). This is a project-page housekeeping issue, not a ticket blocker. The backing story note exists and is well-structured.
- [x] arch:keycloak label -- present on board item
- [x] arch note verified --
arch-keycloaknote exists in pal-e-docs (id: 1728), tags: architecture, active. Documents client pattern, config location (k3s.tfvars lines 238-257), and future multi-tenant plans. - [x] Forgejo issue -- ldraney/pal-e-services#75, open
File Targets
- [x] terraform/k3s.tfvars (lines 238-257) -- verified: pal-enterprises client block exists at exactly lines 238-257. Contains valid_redirect_uris (line 246-248) and web_origins (line 249-251) as described. Currently only has prod URI. File is gitignored (*.tfvars in .gitignore), which is correct for secrets.
- [x] services block (lines 369-376) -- verified: pal-enterprises ArgoCD service entry already exists with forgejo_repo, image_repo, port 3000, funnel true, source_repo, and source_path pointing to overlays/pal-enterprises/prod. tofu apply will create the ArgoCD Application resource from this existing config.
Repo Placement
OK. Issue filed on ldraney/pal-e-services which houses terraform/. Both keycloak client config and ArgoCD app creation live in this repo's terraform state. No cross-repo work needed.
Dependencies
- Upstream (done): pal-e-deployments#156 (prod overlay) -- board item #1184, column: done. Prerequisite for ArgoCD app source_path. Confirmed merged.
- Downstream: T2 (board item #1197, pal-e-deployments#158, dev overlay) -- depends on Keycloak redirect being configured first.
- Parallel: T3 (board item #1201, pal-e-platform#360, Arch base image) -- Wave 1 parallel, no dependency.
- Related: Board item #1192 (Keycloak upgrade to 26.x) -- shares arch:keycloak label but independent work in pal-e-platform.
- Dependencies are now documented in the issue body. Previously undocumented.
Acceptance Criteria
5 criteria, all concrete and agent-verifiable:
- AC1-2: Verifiable by inspecting tfvars after edit
- AC3: Verifiable by running tofu apply (requires local access)
- AC4: Verifiable via kubectl command (provided in AC)
- AC5: Verifiable via Keycloak admin console (requires browser/API access)
AC3-5 require runtime access, expected for infra tickets. No missing criteria.
Blast Radius
- Adding redirect URIs is additive -- no existing URIs modified or removed.
- ArgoCD app creation is additive -- new application entry, does not modify existing ones.
- Test expectation #2 constrains: "No other Keycloak clients or services affected."
- tofu plan step will verify blast radius before apply.
- Checked all other clients in k3s.tfvars -- westside-admin pattern (multi-URI) is well-established, pal-enterprises follows it.
Decomposition Assessment
1 file target, 1 repo, 5 AC, estimated agent work under 5 minutes. No decomposition needed.
Previous Review Resolution
Previous Recommendation Status [BODY] Add Checklist section FIXED -- Checklist added with infra-appropriate items [SCOPE] Create story-infra-overhaul note FIXED -- note exists (id: 1727) with ticket table and outcomes [SCOPE] Create arch-keycloak note FIXED -- note exists (id: 1728) with client pattern and config location [BODY] Fix broken sop-keycloak-client-creation reference FIXED -- removed, replaced with actual references Recommendations
- [SCOPE] Add infra-overhaul row to project-pal-enterprises user-stories table -- story note exists but is not registered on the project page. Non-blocking; does not affect this ticket's executability.
No blocking issues. Ticket is ready for execution.
-
Review: T3: Arch Linux Ruby base image in Harbor
review-1201-2026-05-10Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- present (Infra)
- [x] Lineage -- present
- [x] Repo -- present (ldraney/pal-e-platform)
- [x] User Story -- present
- [x] Context -- present, thorough
- [x] File Targets -- present (2 creates, 1 conditional modify)
- [x] Acceptance Criteria -- present (7 items)
- [x] Test Expectations -- present (4 items)
- [x] Constraints -- present
- [x] Related -- present
- [x] Dockerfile Shape -- present (bonus: inline code reference)
- [x] Pipeline Shape -- present (bonus: inline description)
- [ ] Checklist -- missing (PR opened, tests pass, no unrelated changes)
Traceability
- [x] story:infra-overhaul label -- present on board item
- [ ] story note MISSING -- [SCOPE] The
infra-overhaulstory is not listed in theproject-pal-enterprisesuser-stories table. Current stories: landing-page, sso-gateway, tool-dashboard, client-portal. Add infra-overhaul row. - [x] arch:harbor label -- present on board item
- [ ] arch note MISSING -- [SCOPE] No
arch-harbornote found in pal-e-docs. Create architecture note for the Harbor component. - [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/pal-e-platform/issues/360, state: open
File Targets
- [x]
docker/ruby-arch/Dockerfile-- to be CREATED in pal-e-platform. Directory does not exist yet (expected for new file). Dockerfile shape provided inline. - [x]
docker/ruby-arch/.woodpecker.yaml-- to be CREATED in pal-e-platform. Pipeline shape described inline. - [x]
terraform/modules/harbor/main.tf-- VERIFIED EXISTS in pal-e-platform. Currently defines Harbor Helm release, OIDC config, and portal proxy. No Harbor project resource exists yet -- the issue correctly notes "add pal-e Harbor project (if not exists)".
Repo Placement
OK. Issue is filed on
ldraney/pal-e-platformand all file targets (docker/, terraform/) are pal-e-platform paths. The board item is onboard-pal-enterpriseswhich is correct since this infra work supports the pal-enterprises app. No cross-repo issues.Dependencies
The issue is Ticket 3 of a 5-ticket infra overhaul sequence (T1-T5). Dependencies found but NOT documented in the issue body:
- T4 (Dockerfile migration to Arch base, #1198) -- directly depends on T3. Cannot migrate app Dockerfiles to Arch base until the base image exists in Harbor.
- T2 (Dev overlay with Arch base, #1197) -- depends on T3. Dev overlay references "Arch base" which is this image.
- T1 (Keycloak dev redirect, #1200) -- independent, no dependency on T3.
- T5 (Woodpecker CI with test gates, #1199) -- loosely related. CI pipeline conventions from T5 may affect T3's pipeline yaml, but T3 should be done first.
[BODY] Add a Dependencies section listing T4 and T2 as downstream dependents.
Acceptance Criteria
7 AC items are specific and testable. 4 test expectations are concrete with runnable commands. However:
- The Constraints section flags a potential Kaniko compatibility issue with
pacman -Syubut this is not reflected in the AC. If Kaniko cannot run pacman, the entire approach changes (buildah or docker-in-docker). This should be validated first or added as AC. - No AC for Harbor project creation via Terraform (the
pal-eproject in Harbor).
Blast Radius
Low immediate blast radius -- this creates new files only. The existing pal-enterprises Dockerfile (
ruby:3.4.8-slimDebian base) is untouched by this ticket. Future consumers listed in the issue (pal-enterprises, westside-ror, pal-e-ror, westside-docs) will be migrated by T4 separately. No existing pipelines are modified.The existing
.woodpecker.yamlin pal-enterprises uses Kaniko for builds (plugin-kaniko:2.3.0). The pal-e-platform.woodpecker.yamluses OpenTofu, not container builds. The new pipeline indocker/ruby-arch/.woodpecker.yamlwould be a new Woodpecker pipeline definition -- verify whether Woodpecker auto-discovers yaml files in subdirectories or if this needs explicit registration.Decomposition Assessment
3 file targets in 1 repo. 7 AC + 4 test expectations. Estimated agent work is within 5 minutes -- the Dockerfile and pipeline yaml are straightforward with shapes provided. No decomposition needed.
Recommendations
- [SCOPE] Add
infra-overhauluser story entry toproject-pal-enterprisesuser-stories table. - [SCOPE] Create architecture note
arch-harborfor the Harbor component in pal-e-docs. - [BODY] Add Checklist section (PR opened, tests pass, no unrelated changes).
- [BODY] Add Dependencies section documenting T4 and T2 as downstream dependents.
- [BODY] Add AC for Harbor project creation: "[ ] Harbor project
pal-eexists (created via Terraform if needed)." - [BODY] Add AC or spike note for Kaniko + pacman compatibility: "[ ] Verify
pacman -Syuworks inside Kaniko build (or document alternative build tool)."
-
Review: T1: Keycloak dev redirect + ArgoCD app
review-1200-2026-05-10Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- "Infra" (maps to Task base template)
- [x] Lineage -- "Plan: pal-enterprises infrastructure overhaul (Ticket 1 of 5)"
- [x] Repo -- ldraney/pal-e-services
- [x] User Story -- developer dev-environment auth flow
- [x] Context -- clear motivation, references existing pattern
- [x] File Targets -- terraform/k3s.tfvars with line references
- [x] Acceptance Criteria -- 5 items, all concrete
- [x] Test Expectations -- 2 items, verifiable
- [x] Constraints -- 3 items, clear
- [x] Related -- 3 references with context
- [ ] Checklist -- MISSING (PR opened, tests pass, no unrelated changes)
Traceability
- [x] story:infra-overhaul label -- present on board item
- [ ] story note MISSING -- [SCOPE] "infra-overhaul" is not listed in project-pal-enterprises user-stories section. Only landing-page, sso-gateway, tool-dashboard, client-portal exist. Create user story entry on project-pal-enterprises.
- [x] arch:keycloak label -- present on board item
- [ ] arch note MISSING -- [SCOPE] No arch-keycloak note found in pal-e-docs. Create architecture note arch-keycloak for the Keycloak component.
- [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/pal-e-services/issues/75, open
File Targets
- [x] terraform/k3s.tfvars -- file is gitignored (*.tfvars in .gitignore), which is correct for secrets. Structure verified via k3s.tfvars.example. The keycloak_clients variable type confirms valid_redirect_uris (list(string)) and web_origins (list(string)) fields exist.
- [x] Line references (238-257) -- cannot verify exact lines from repo (gitignored), but the pattern is confirmed: westside-admin client in k3s.tfvars.example shows the established multi-URI pattern with both prod and dev redirect URIs.
Note: Since k3s.tfvars is not tracked in git, the agent will need local filesystem access to the pal-e-services terraform directory to make this change. This is an operational constraint, not a scope issue.
Repo Placement
OK. Issue is filed on ldraney/pal-e-services, which houses the terraform/ directory. The keycloak client config and ArgoCD app creation both live in this repo's terraform state. No cross-repo work needed.
Dependencies
- Upstream (done): pal-e-deployments#156 (prod overlay) -- closed, already merged. This is a prerequisite for the ArgoCD app to have a source_path to point to.
- Downstream: T2 (board item #1197, dev overlay) depends on Keycloak redirect being configured first, since the dev environment needs auth to work.
- Related: T5 (board item #1199, Woodpecker CI / pal-enterprises#7) -- independent, no dependency.
- Related: Board item #1192 (Keycloak upgrade to 26.x) -- shares arch:keycloak label but is independent work in pal-e-platform.
- Not documented in scope: The ArgoCD app creation requires a services entry in k3s.tfvars (per services.tf pattern). The issue mentions ArgoCD app but does not list the services block as a file target. However, since k3s.tfvars is a single file, this is covered by the existing file target.
Acceptance Criteria
5 criteria, all concrete and agent-verifiable:
- AC1-2: Verifiable by inspecting tfvars after edit
- AC3: Verifiable by running tofu apply (requires local access)
- AC4: Verifiable via kubectl command (provided in AC)
- AC5: Verifiable via Keycloak admin console (requires browser/API access)
AC3 and AC5 require runtime access, not just code changes. This is expected for infra tickets.
Blast Radius
- Test expectation #2 explicitly constrains: "No other Keycloak clients or services affected."
- The tofu plan step (Test Expectation #1) will verify blast radius before apply.
- The westside-admin pattern is well-established -- adding redirect URIs to an existing client is a safe, additive change.
- The ArgoCD app creation is also additive -- it creates a new application entry, does not modify existing ones.
- No downstream consumers affected beyond the pal-enterprises app itself.
Decomposition Assessment
1 file target, 1 repo, 5 AC, estimated agent work under 5 minutes. No decomposition needed.
Broken Reference
The issue's Related section references
sop-keycloak-client-creationbut this note does not exist in pal-e-docs. This is informational -- it does not block the ticket but the SOP should either be created or the reference removed.Recommendations
- [BODY] Add missing Checklist section (PR opened, tests pass, no unrelated changes)
- [SCOPE] Create user story entry "infra-overhaul" on project-pal-enterprises user-stories section -- this story is used by 5 tickets (T1-T5) on this board
- [SCOPE] Create architecture note arch-keycloak for the Keycloak component -- referenced by this ticket and board item #1192
- [BODY] Fix or remove broken reference to sop-keycloak-client-creation in Related section
-
Review: Woodpecker CI pipeline (.woodpecker.yaml)
review-1185-2026-05-09Verdict: APPROVED
Re-review after refinement. Previous verdict was NEEDS_REFINEMENT due to missing
arch-rails-appnote. That note now exists (slug:arch-rails-app, taggedarchitecture,active, projectpal-enterprises). All prior findings remain valid. No new issues found.Template Completeness
- [x] Type -- present (Infra; non-standard but maps to base template-issue)
- [x] Lineage -- present
- [x] Repo -- present
- [x] User Story -- present
- [x] Context -- present
- [x] File Targets -- present
- [x] Acceptance Criteria -- present
- [x] Test Expectations -- present
- [x] Constraints -- present
- [x] Checklist -- present
- [x] Related -- present
Traceability
- [x] story:sso-gateway label -- "Single login grants access to all platform tools"
- [x] story note verified -- found in project-pal-enterprises user-stories section (key: sso-gateway, role: Authenticated user)
- [x] arch:rails-app label -- Rails application component
- [x] arch note verified --
arch-rails-appexists in pal-e-docs (slug: arch-rails-app, tagged architecture,active, project pal-enterprises) - [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/pal-enterprises/issues/7, open
File Targets
- [x]
.woodpecker.yaml(to create) -- verified: does not exist yet, correct - [x]
Dockerfile(NOT to touch) -- verified: exists at repo root, Rails production build with Thruster, EXPOSE 80 - [x]
k8s/dev.yaml(NOT to touch) -- verified: exists, dev-only manifest
Repo Placement
OK. Issue filed on ldraney/pal-enterprises, fix is in the same repo. Single-repo change.
Dependencies
- Board item #1183 (NetworkPolicy: allow pal-enterprises to Postgres + Keycloak) -- in
todocolumn. Not a blocker for CI pipeline itself, but required for the deployed app to function. - Board item #1184 (Kustomize overlay: overlays/pal-enterprises/prod/) -- in
todocolumn. Not a blocker for CI, but required for ArgoCD to deploy the image CI produces. - Manual gates documented in checklist: Woodpecker UI activation and Harbor secrets. These are prerequisites for pipeline execution but not for the file creation PR.
- Issue states Harbor project must exist first (created by tofu apply) -- this is a pre-existing dependency, correctly documented.
Acceptance Criteria
3 ACs, all testable by an agent or via Woodpecker MCP:
.woodpecker.yamlexists in repo root -- file existence check, trivially verifiable- Uses internal Harbor URL (
harbor-core.harbor.svc.cluster.local) per SOP -- grep verifiable. Note: existing sibling pipelines (e.g. westside-admin) useharbor.harbor.svc.cluster.local. The issue follows the SOP, but the implementing agent should confirm which internal URL is correct for this cluster. - Pushes
pal-enterprises/app:{SHA}tag on merge to main -- verifiable via pipeline output and Harbor API
Test expectations are clear and actionable. Run command references Woodpecker MCP tool.
Blast Radius
Low. Single new file creation (
.woodpecker.yaml). No existing code is modified. No downstream consumers affected. Sibling repos (westside-admin, platform-validation, etc.) have their own independent pipelines -- no shared CI config.Decomposition Assessment
1 file target, 3 acceptance criteria, single repo. Well under the 5-minute rule. No decomposition needed.
Recommendations
No action needed.
Re-Review History
- 2026-05-09 (v1): NEEDS_REFINEMENT -- [SCOPE] Create architecture note
arch-rails-app. - 2026-05-09 (v2): APPROVED --
arch-rails-appnote confirmed to exist (slug: arch-rails-app, tags: architecture,active, project: pal-enterprises). Previous search_notes query returned empty but list_notes with tag/project filter and direct get_note both confirm the note exists. All traceability legs complete.
-
Review: Client-scoped read-only access to pal-e-docs project boards
review-1190-2026-05-09Verdict: READY
Re-review after three refinements applied. Previous verdict was NEEDS_REFINEMENT.
Template Completeness
- [x] Type -- Spike
- [x] Lineage -- Standalone, with session context
- [x] Repo -- Multiple: ldraney/pal-enterprises, pal-e-docs, pal-e-services
- [x] Question -- Clear either/or framing (native multi-tenancy vs proxy vs hybrid)
- [x] What to Explore -- Six bullet areas covering API surface, two option paths, Keycloak mapping, Forgejo access, read-only enforcement
- [x] Success Criteria -- Four items, properly framed for a spike (decision + follow-up tickets)
- [x] Time-box -- "1 session"
- [x] Related -- References project page, related issues (#12, #9), and Keycloak infra dependencies (#357, #358)
All required spike template sections present and complete.
Traceability
- [x] story:client-portal label -- present on board item
- [x] story note verified -- found in project-pal-enterprises user-stories table (key=client-portal, Role=Client, Success Metric="Client can view their project board and active work via read-only agency link")
- [x] arch:multi-tenant label -- present on board item
- [x] arch note verified -- arch-multi-tenant note exists in pal-e-docs (placeholder, content pending spike completion). Tags: architecture, active. Project: pal-enterprises.
- [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/pal-enterprises/issues/13, state: open
All three traceability legs verified. Previous gaps (missing story note, missing arch note) have been resolved.
File Targets
N/A -- Spike type. No file targets expected or present. Correct per template.
Repo Placement
OK. Issue is filed on ldraney/pal-enterprises. Issue body correctly identifies this as a multi-repo investigation (pal-enterprises, pal-e-docs, pal-e-services). For a spike, filing on the primary consumer repo is appropriate.
Dependencies
- #12 (board item 1189) -- "Create pal-enterprises-docs RoR repo" (5pt feature, backlog). Soft dependency -- spike decision shapes #12 implementation. Documented in Related.
- #9 (board item 1187) -- "Owner dashboard: lead pipeline + client management" (5pt feature, backlog). Admin counterpart to client portal. Soft dependency. Documented in Related.
- pal-e-platform #357 (board item 1183) -- "NetworkPolicy: allow pal-enterprises to Postgres + Keycloak" (2pt infra, todo). Prerequisite for Keycloak testing. Now documented in Related.
- pal-e-platform #358 (board item 1192) -- Keycloak infra (3pt, backlog). Related to Keycloak mapping exploration. Now documented in Related.
All dependencies now documented in the issue body's Related section. Previous gap (#357, #358 missing from Related) has been resolved.
Acceptance Criteria
The spike uses "Success Criteria" (correct for spike type). All four criteria are investigation-oriented and agent-verifiable:
- "Architecture decision documented" -- check for decision note artifact
- "Keycloak role mapping strategy defined" -- check for documented strategy
- "Follow-up feature ticket(s) created" -- check for new Forgejo issues
- "Or: no action if simpler alternative discovered" -- check for closing comment
Blast Radius
Acceptable for a spike. Investigation only, no code changes. Follow-up tickets should scope blast radius carefully (pal-e-docs API changes, Keycloak realm config, Forgejo auth integration).
Decomposition Assessment
Spike type, time-boxed to 1 session. No file targets, no code changes. 4 success criteria, all investigation-oriented. Single agent can complete in one pass. No decomposition needed.
Refinements Applied (since previous review)
- [x] [SCOPE]
story:client-portaladded to project-pal-enterprises user-stories table -- VERIFIED - [x] [SCOPE]
arch-multi-tenantarchitecture note created as placeholder -- VERIFIED (slug: arch-multi-tenant, note_type: doc, tags: architecture/active) - [x] [BODY] Related section updated to include #357 and #358 (Keycloak infra dependencies) -- VERIFIED
Recommendations
No action needed. All previous refinements verified. Ticket is ready for next_up.
-
Review: Repo hygiene: gitignore cleanup and SOP enforcement
review-1193-2026-05-09Verdict: READY
Template Completeness
- [x] Type -- Task
- [x] Lineage -- Standalone, discovered during production validation
- [x] Repo -- ldraney/pal-enterprises
- [x] User Story
- [x] Context
- [x] File Targets -- present (template suggests "Scope" for Task type, but File Targets is appropriate here since specific files are named)
- [x] Acceptance Criteria -- 3 criteria
- [x] Test Expectations -- 3 test commands
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x] story:sso-gateway label -- found in project-pal-enterprises user-stories section
- [x] story note verified -- sso-gateway entry exists in project-pal-enterprises user-stories table
- [ ] arch:rails-app label -- arch note MISSING -- [SCOPE] Create architecture note arch-rails-app for component rails-app
- [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/pal-enterprises/issues/11, open
File Targets
- [x]
.gitignore-- verified: file exists..claude/is already present on line 34..claude-no-enforceis NOT currently in .gitignore (so removal is a no-op). - [x]
.claude-no-enforce-- verified: file exists (empty, 0 bytes), needs deletion.
Note: Two of the three acceptance criteria are already satisfied in the current codebase state:
.claude/is already in.gitignore(line 34).claude-no-enforceis not in.gitignore(never was)
The only actual work is deleting the
.claude-no-enforcefile. This does not invalidate the ticket -- the AC and test expectations will still pass after deletion -- but the agent should be aware only one change is needed.Repo Placement
OK. Issue is filed on ldraney/pal-enterprises and all file targets are in that repo. Single-repo scope.
Dependencies
No blocking dependencies found. All other board items are in backlog, todo, or done. This ticket is independent and can proceed without waiting on anything. No other tickets depend on this one.
Acceptance Criteria
All three criteria are machine-verifiable with simple shell commands. Test expectations map 1:1 to acceptance criteria. The grep and test commands are correct and will produce the expected results after the single deletion.
Blast Radius
Minimal. No references to
.claude-no-enforcefound anywhere in the codebase (searched .rb, .yml, .yaml, .json, .sh, .md files). The.dockerignoredoes not reference it either. Deleting the file has no downstream effects beyond re-enabling SOP enforcement hooks, which is the intent.Decomposition Assessment
No decomposition needed. 1 file to delete, 0 files to edit (both gitignore changes are already done). Well under the 5-minute rule. 3 acceptance criteria, single repo.
Recommendation
No action needed. Scope is clean and the ticket is ready for implementation. The arch note gap is a platform-wide concern, not a blocker for this ticket.
- [SCOPE] Create architecture note arch-rails-app for component rails-app (platform-wide gap, not specific to this ticket)
-
Review: Render docs/ markdown + mermaid at -docs URL
review-1186-2026-05-09Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Standalone
- [x] Repo -- ldraney/pal-enterprises
- [x] User Story -- present, well-formed
- [x] Context -- present, adequate
- [x] File Targets -- 4 files listed
- [x] Acceptance Criteria -- 5 criteria
- [x] Test Expectations -- present
- [x] Constraints -- present
- [x] Checklist -- present
- [x] Related -- present
Traceability
- [x] story:sso-gateway label -- Single login grants access to all platform tools
- [x] story note verified -- found in project-pal-enterprises user-stories section (row: sso-gateway, role: Authenticated user)
- [x] arch:rails-app label -- Rails application component
- [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails-app for component rails-app
- [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/pal-enterprises/issues/8, open
File Targets
- [x]
app/controllers/docs_controller.rb-- to be created, does not exist yet (correct) - [x]
app/views/docs/-- to be created, directory does not exist yet (correct) - [x]
config/routes.rb-- verified exists, no /docs route present yet (correct) - [x]
Gemfile-- verified exists, no markdown gem present yet (correct) - [x]
docs/content files -- verified: README.md, onboarding.md, architecture.md, dashboards.md all present - [ ]
docs/mermaid content -- NOTE: no docs currently contain mermaid diagrams, so AC #3 (mermaid rendering) cannot be visually validated against existing content. Consider adding a sample mermaid block to one doc file, or noting this limitation.
Repo Placement
OK. Issue filed on ldraney/pal-enterprises, all file targets are in the same repo. Single-repo scope.
Dependencies
- No blocking dependencies on the board. This ticket is independent of other backlog items.
- Auth infrastructure (Keycloak OIDC, require_login pattern) already exists and is in done column (Phase 3: Keycloak OIDC authentication, item #1180).
- No test directory exists (
test/andspec/both missing). The Test Expectations section referencesbin/rails testbut no test infrastructure is set up. The implementing agent will need to create the test directory and test helper as part of the work, or this should be a separate prerequisite ticket.
Acceptance Criteria
- [x] AC1: /docs renders docs/README.md as index -- verifiable via integration test
- [x] AC2: /docs/:slug renders individual docs -- verifiable via integration test
- [ ] AC3: Mermaid diagrams render -- NOT verifiable against current docs content (no mermaid blocks exist). Agent can verify mermaid.js is included in the view, but cannot confirm visual rendering without test content.
- [x] AC4: Route behind Keycloak auth -- verifiable via redirect test for unauthenticated user
- [x] AC5: Navigation between docs -- verifiable via link presence in rendered HTML
Missing AC: No acceptance criterion for path traversal protection. A slug like
../../etc/passwdor../config/secretsmust be rejected. This is a security-critical gap for a controller that reads files from the filesystem.Blast Radius
- No existing markdown rendering or file-serving patterns in the app. This is net-new functionality.
- Security risk: the docs controller will read files from disk based on user-supplied slugs. Path traversal protection is not mentioned in Constraints or AC. The implementing agent must sanitize the slug parameter to prevent directory traversal attacks.
- No downstream consumers affected -- this is a new route.
Decomposition Assessment
4 file targets in 1 repo, 5 acceptance criteria. Fits within a single agent pass (estimated ~5 minutes). No decomposition needed.
Recommendations
[BODY]Add acceptance criterion: "Requests for slugs containing path traversal sequences (e.g.,../) return 404 or 400, not file contents outside docs/"[BODY]Add constraint: "Sanitize slug parameter to prevent directory traversal -- reject any slug containing..,/, or characters outside[a-z0-9_-]"[BODY]Note in Test Expectations that test/ directory does not exist yet and must be created (or rely on Rails generator defaults)[SCOPE]Create architecture note arch-rails-app for component rails-app[SCOPE]The conventionconvention-client-project-structurereferenced in Context and Related does not exist in pal-e-docs. Create it or update the reference.
-
Review: Phase 2: Contact form (business name, name, logo, email) + calendar redirect
review-1179-2026-05-09Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- "Decomposed from #1 -- Phase 2 of 4. Depends on #2 (scaffold)."
- [x] Repo -- present but wrong (see Repo Placement below)
- [x] User Story
- [x] Context
- [x] File Targets
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
All required sections are present per
template-issue-feature.Traceability
- [x] story:landing-page label -- "Visitor: Can understand services offered and submit contact form"
- [x] story note verified -- found in project-pal-enterprises user-stories section
- [ ] arch:rails-app label -- present on board item
- [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails-app for the Rails application component
- [x] Forgejo issue -- ldraney/pal-enterprises#3, state: open
File Targets
Major discrepancy: the issue lists files "to create" but several already exist from a prior implementation pass. The existing implementation uses a DB-backed
Leadmodel, which contradicts the issue's "no database model" constraint.- [ ]
app/controllers/contacts_controller.rb-- ISSUE: Listed as "to create" but already exists. Uses Lead model (DB-backed), contradicts spec's "no database model" constraint. - [ ]
app/views/contacts/new.html.erb-- ISSUE: Listed as "to create" but already exists. - [ ]
app/views/contacts/thank_you.html.erb-- Does not exist. Controller redirects to root_path instead of rendering a thank_you page. Spec and implementation disagree. - [ ]
app/mailers/contact_mailer.rb-- Does not exist. Only application_mailer.rb present. This is genuinely needed. - [ ]
app/views/contact_mailer/new_inquiry.html.erb-- Does not exist. Genuinely needed. - [x]
config/routes.rb-- Verified: already hasresources :contacts, only: [:new, :create] - [x]
app/views/pages/home.html.erb-- Verified: already renders the contact form partial inline. - [x]
app/views/layouts/application.html.erb-- Verified: already has "Contact" nav link at line 23.
Additional file not in spec:
app/views/contacts/_form.html.erb(partial) andapp/models/lead.rb(DB model with validations) already exist.Repo Placement
MISMATCH. The issue body says
forgejo_admin/pal-enterprisesbut the actual repo isldraney/pal-enterprises. Theforgejo_admin/pal-enterprisespath returns no data from the API. The board item's Forgejo URL also uses theforgejo_adminpath. This needs correction.Dependencies
- Phase 1 (#1178, "Rails scaffold, landing page, health check") is in
done-- dependency satisfied. - Phase 3 (#1180, "Keycloak OIDC authentication") is in
done-- no blocker. - Board item #1188 ("Remove Tailwind, implement plain CSS design system") is in
backlog. The current issue spec references Tailwind styling ("contact form with Tailwind styling"). If #1188 lands first, Tailwind references become invalid. If this ticket lands first, the Tailwind work will need to update these views. Order dependency is undocumented. - No items currently block this ticket.
Acceptance Criteria
Mixed testability:
- "Contact form at /contact renders with all fields" -- testable, but spec says name/email/message_type/message_body while codebase has name/email/message (no message_type select). Criteria ambiguous about which fields.
- "Form validates presence of name, email, and message" -- testable. Already implemented in Lead model.
- "Successful submission delivers email to admin" -- testable, but mailer does not exist yet. This is the real remaining work.
- "Turbo-powered submit shows confirmation without full page reload" -- testable but NOT implemented. No turbo_frame or turbo_stream usage in the contact form.
- "Invalid submission re-renders form with errors" -- testable. Already implemented.
- "Landing page links to contact form" -- testable. Already implemented (form rendered inline on home page, plus nav link).
Missing AC: The board item title says "business name, name, logo, email + calendar redirect" but none of these extra fields (business_name, logo upload, calendar redirect) appear in the Forgejo issue or the codebase. The board title and spec are misaligned.
Blast Radius
Low. The contact form is self-contained. Adding Action Mailer touches SMTP configuration which could affect any future mailers but has no current downstream consumers. The Tailwind dependency is the main cross-cutting concern (see Dependencies).
Decomposition Assessment
6 acceptance criteria (borderline), but all within a single repo and a small number of files. The real remaining work is: (1) add mailer, (2) add message_type select, (3) add Turbo submit, (4) decide DB vs stateless. This fits in a single agent pass if the scope contradictions are resolved first. No decomposition needed.
Recommendations
- [BODY] Fix Repo field:
forgejo_admin/pal-enterprisesshould beldraney/pal-enterprises. - [BODY] Update File Targets to reflect current codebase state: mark existing files as "to modify" not "to create." Add
app/models/lead.rbandapp/views/contacts/_form.html.erbto the target list. - [BODY] Resolve DB vs stateless contradiction: the Constraints section says "No database model -- contact form is stateless" but the codebase already has a
Leadmodel backed by aleadstable. Either update the constraint to accept the DB model, or rewrite the controller to use a plain PORO + mailer. - [BODY] Resolve board title vs issue scope mismatch: board says "business name, name, logo, email + calendar redirect" but issue spec has "name, email, message type, message body" with no logo upload or calendar redirect. Either update the board title or add the missing fields to the issue.
- [BODY] Add message_type select dropdown to file targets and form spec (currently missing from implementation).
- [BODY] Clarify Tailwind dependency: note that views use Tailwind classes, and document ordering relative to board item #1188 (Tailwind removal).
- [SCOPE] Create architecture note
arch-rails-appfor the Rails application component.
-
Review: Phase 4: Authenticated dashboard with tool grid
review-1181-2026-05-09Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type -- Feature
- [x] Lineage -- Decomposed from #1, depends on #4
- [x] Repo -- forgejo_admin/pal-enterprises
- [x] User Story -- present, well-formed
- [x] Context -- present, clear motivation
- [x] File Targets -- present, lists creates and modifies
- [x] Acceptance Criteria -- 6 criteria listed
- [x] Test Expectations -- 2 unit tests + run command
- [x] Constraints -- present
- [x] Checklist -- present
- [x] Related -- present
All required template sections present. Template is complete.
Traceability
- [x] story:tool-dashboard label -- "Dashboard shows all tools with live status"
- [x] story note verified -- found in project-pal-enterprises user-stories section (key: tool-dashboard, role: Authenticated user)
- [x] arch:rails-app label -- Rails application component
- [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails-app for component rails-app. No matching note found in pal-e-docs.
- [x] Forgejo issue -- ldraney/pal-enterprises#5, open
File Targets
Files to create (per issue):
- [x]
app/controllers/dashboard_controller.rb-- verified: EXISTS already. Containsbefore_action :require_loginandindexaction. Issue says "create" but file already exists from prior work. - [x]
app/views/dashboard/index.html.erb-- verified: EXISTS already. Contains a basic dashboard view with user info and sign-out link. However, does NOT contain the tool card grid specified in the issue. - [ ]
app/controllers/concerns/authentication.rb-- ISSUE: File does NOT exist. The concerns directory contains only.keep. Auth methods (require_login,current_user,logged_in?) are defined directly inApplicationControllerinstead. The issue should be updated to reflect this -- either extract to a concern as planned, or document that the methods live in ApplicationController.
Files to modify (per issue):
- [x]
config/routes.rb-- verified: EXISTS. Already containsget "dashboard", to: "dashboard#index". - [x]
app/controllers/sessions_controller.rb-- verified: EXISTS. Already redirects todashboard_pathafter login.
Key finding: Most of the plumbing (controller, route, session redirect) already exists from the Phase 3 work. The main remaining work is the tool card grid in the dashboard view. The issue's file targets are partially stale.
Repo Placement
Issue filed on
forgejo_admin/pal-enterprisesbut that repo returns HTTP 301 -- it was transferred toldraney/pal-enterprises. The board item'sforgejo_issue_urlstill points to the old org. The actual issue lives atldraney/pal-enterprises#5. The issue body's### Reposection also saysforgejo_admin/pal-enterpriseswhich is stale. Single-repo fix, no cross-repo concerns.Dependencies
- Phase 3 (Keycloak OIDC auth, board item #1180) -- in
donecolumn. Dependency satisfied. - No blocking items found in
in_progresscolumn. - No items currently depend on this ticket.
- Dependencies are documented in the issue Lineage section ("Depends on #4").
Acceptance Criteria
- [x] "Dashboard renders at /dashboard with tool card grid" -- testable, but current view has NO tool cards. This is the main work item.
- [x] "Each card shows tool name, description, status, and link" -- testable. 10 tools listed in context. "Status" is ambiguous -- does it mean live health check or static label?
- [x] "Unauthenticated access to /dashboard redirects to /" -- MINOR: current implementation redirects to
/loginnot/. Either the AC or the implementation needs alignment. - [x] "Dashboard shows logged-in user's name" -- testable, already implemented in current view.
- [x] "Successful login redirects to /dashboard" -- testable, already implemented in SessionsController.
- [x] "All platform tools listed with correct Tailscale URLs" -- testable but the 10 tool URLs are not specified in the issue. Agent would need to discover them.
5 of 6 AC are verifiable by an agent. The "status" field on tool cards is ambiguous.
Blast Radius
Low blast radius. Changes are confined to the dashboard view and controller. No other controllers reference dashboard. No downstream consumers. The auth guard pattern (
before_action :require_login) is already established and tested via Phase 3.Decomposition Assessment
File count: 1-3 files in 1 repo. AC count: 6. Estimated agent work: under 5 minutes -- mostly building the tool card grid HTML. No decomposition needed.
Recommendations
[BODY]Update file targets: most files already exist. Markdashboard_controller.rb,routes.rb, andsessions_controller.rbas already complete. Primary remaining work is the tool card grid inapp/views/dashboard/index.html.erb.[BODY]Remove or updateapp/controllers/concerns/authentication.rbfile target. Auth methods are inApplicationControllerdirectly. Either extract to concern as planned or update the issue to reflect reality.[BODY]Fix repo reference:forgejo_admin/pal-enterprises->ldraney/pal-enterprisesin the### Reposection.[BODY]Clarify AC: "redirects to /" -- current code redirects to/login. Update AC to match desired behavior.[BODY]Clarify AC: "status" on tool cards -- specify whether this means a static label or live health check.[BODY]Add Tailscale URLs for the 10 tools so the agent doesn't need to discover them.[SCOPE]Create architecture notearch-rails-appfor the rails-app component.
-
Review: Upgrade Ruby, Keycloak, and OmniAuth versions
review-1182-2026-05-09Verdict: NEEDS_REFINEMENT
Template Completeness
- [x] Type — Feature
- [x] Lineage — Standalone, discovered during scaffold
- [x] Repo — ldraney/pal-enterprises
- [x] User Story — present, well-formed
- [x] Context — thorough, includes version table
- [x] File Targets — present with modify/don't-touch lists
- [x] Acceptance Criteria — 5 criteria
- [x] Test Expectations — 5 items with run command
- [x] Constraints — 4 constraints listed
- [x] Checklist — present
- [x] Related — present
All required sections for the Feature template are present.
Traceability
- [x] story:infra-upgrades label — present on board item
- [ ] story note MISSING — [SCOPE] The project-pal-enterprises user-stories section has entries for landing-page, sso-gateway, and tool-dashboard, but no entry for infra-upgrades. Create user story entry on project-pal-enterprises.
- [x] arch:keycloak label — present on board item
- [ ] arch note MISSING — [SCOPE] No arch-keycloak note found in pal-e-docs. Create architecture note arch-keycloak for the Keycloak component.
- [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/pal-enterprises/issues/6, state: open
File Targets
- [x]
.ruby-version— verified: exists, currently containsruby-3.4.8 - [x]
Gemfile— verified: exists, contains omniauth stack (omniauth_openid_connect, omniauth-rails_csrf_protection) - [x]
Gemfile.lock— verified: exists, shows omniauth 1.9.2 (matches issue's "Current" column) - [x]
k8s/dev.yaml— verified: exists, showsimage: ruby:3.4-slimat line 47 - [x]
Dockerfile— verified: exists, showsARG RUBY_VERSION=3.4.8andruby:$RUBY_VERSION-slim - [x]
pal-e-platform: terraform/modules/keycloak/main.tf— verified: exists, showsimage = "quay.io/keycloak/keycloak:26.0.7"at line 100 - [x]
pal-e-services: terraform/k3s.tfvars— verified: exists, contains keycloak realm and client config
All file targets verified against the codebase. Paths are accurate and contents match what the issue claims.
Repo Placement
The Forgejo issue is filed on ldraney/pal-enterprises, which is the primary repo. However, the issue scope explicitly touches THREE repos:
ldraney/pal-enterprises— Ruby version, Gemfile, Dockerfile, k8s/dev.yamlldraney/pal-e-platform— Keycloak image tag in terraformldraney/pal-e-services— potential realm schema changes in terraform
[DECOMPOSE] — Three repos affected. The Keycloak upgrade (pal-e-platform) and the Ruby/OmniAuth upgrade (pal-enterprises) are independent work streams that should be separate tickets. A single agent pass cannot PR across three repos.
Dependencies
- Keycloak upgrade (26.0.7 to 26.6.1) must be applied and verified BEFORE the OIDC auth flow can be re-tested with the new OmniAuth version.
- The constraint "Host (archbox) Ruby also needs updating — coordinate with westside-ror and pal-e-ror" introduces an undocumented coordination dependency. Both westside-ror and pal-e-ror are currently on Ruby 3.4.8 with the same image patterns.
- No blocking items found on the board — no other items are currently in_progress.
Acceptance Criteria
AC #1-3 are verifiable for the pal-enterprises repo. AC #4 (Keycloak admin console version) requires the pal-e-platform change to be deployed first — this is a cross-repo dependency. AC #5 (test existing westside-app and pal-e-app auth flows) is a blast-radius validation that belongs on the Keycloak upgrade ticket, not the Ruby/OmniAuth ticket.
Missing AC: No acceptance criterion for verifying gem compatibility with Ruby 4.0 (the Constraints section mentions it but there is no testable AC).
Blast Radius
- Keycloak upgrade affects 8+ OIDC clients: westside-app, westside-spa, mcd-tracker-app, mcd-tracker-ios, westside-ai-bot, playme2k, pal-e-app, westside-admin. All use the same Keycloak instance. The issue only mentions testing westside-app and pal-e-app, but all clients need validation.
- Sibling Ruby apps on same version: westside-ror and pal-e-ror both use Ruby 3.4.8 with identical Dockerfile and k8s/dev.yaml patterns. The Constraints section notes "coordinate with westside-ror and pal-e-ror" but the scope does not include those repos.
- Ruby 4.0 is a major version jump: This carries significant gem compatibility risk. The issue acknowledges this in Constraints but does not provide a verification step or fallback plan.
Decomposition Assessment
NEEDS DECOMPOSITION — This ticket violates the 5-minute rule on multiple axes:
- 3+ repos: pal-enterprises, pal-e-platform, pal-e-services (3 repos)
- 5 AC + 5 test expectations: 10 verifiable items total
- Independent work streams: Ruby/OmniAuth upgrade and Keycloak upgrade are logically separate, with different risk profiles and blast radii
Recommended decomposition:
- Sub-ticket 1: Upgrade Keycloak 26.0.7 to 26.6.1 (pal-e-platform) — terraform image tag change, review upgrade guide, validate all OIDC clients
- Sub-ticket 2: Upgrade Ruby 3.4.8 to 4.0.2 + OmniAuth 2.x (pal-enterprises) — .ruby-version, Gemfile, Dockerfile, k8s/dev.yaml, gem compatibility verification
- Sub-ticket 3 (optional): Coordinate Ruby 4.0 across sibling apps — westside-ror, pal-e-ror alignment
Recommendation
[SCOPE]Create user story entry "infra-upgrades" on project-pal-enterprises user-stories section.[SCOPE]Create architecture note arch-keycloak for the Keycloak component.[DECOMPOSE]5 AC + 5 test items across 3 repos. Split into: (1) Keycloak upgrade on pal-e-platform, (2) Ruby/OmniAuth upgrade on pal-enterprises. Route to skill-decompose-ticket.[BODY]Add AC for gem compatibility verification: "When I runbundle installon Ruby 4.0, all gems resolve without errors."[BODY]AC #5 (test existing auth flows) should move to the Keycloak upgrade sub-ticket since it validates the Keycloak change, not the Ruby change.[LABEL]Consider addingarch:rails-applabel since the Ruby/OmniAuth upgrade is primarily a rails-app concern.
-
Review: Remove Tailwind, implement plain CSS design system
review-1188-2026-05-09Verdict: 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 required sections present per template-issue-feature.
Traceability
- [x] story:sso-gateway label -- "Single login grants access to all platform tools"
- [x] story note verified -- sso-gateway entry found in project-pal-enterprises user-stories table (Story Note column reads "TBD" but table entry exists)
- [x] arch:rails-app label -- Rails application component
- [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails-app for the Rails application component. Searched pal-e-docs for "arch-rails-app", no matching note found.
- [x] Forgejo issue -- ldraney/pal-enterprises#10, state: open
File Targets
Files to modify (all verified to exist):
- [x]
Gemfile-- verified: containsgem "tailwindcss-rails"at line 18 - [x]
app/assets/stylesheets/application.css-- verified: exists, will be rewritten with design system - [x]
app/views/layouts/application.html.erb-- verified: contains ~15 Tailwind utility classes (bg-gray-50, text-indigo-600, shadow, etc.) - [x]
app/views/pages/home.html.erb-- verified: contains ~20 Tailwind utility classes across hero, cards, and headings - [x]
app/views/contacts/new.html.erb-- verified: contains Tailwind classes (max-w-lg, text-3xl, etc.) - [x]
app/views/contacts/_form.html.erb-- verified: heavily Tailwind-styled form with ~25 utility classes - [x]
app/views/sessions/new.html.erb-- verified: Tailwind classes on Keycloak sign-in button and layout - [x]
app/views/dashboard/index.html.erb-- verified: grid layout, cards, borders all use Tailwind utilities - [x]
Procfile.dev-- verified: containscss: bin/rails tailwindcss:watchat line 2 - [x]
README.md-- verified: exists
Files to delete (all verified to exist):
- [x]
app/assets/tailwind/application.css-- verified: contains@import "tailwindcss" - [x]
app/assets/builds/tailwind.css-- verified: contains compiled Tailwind v4.2.4 output
Files to NOT touch (verified no changes needed):
- [x]
Dockerfile-- no tailwind references found - [x]
config/-- no tailwind references found
Repo Placement
OK. Issue filed on ldraney/pal-enterprises, all file targets are within the same repo. Single-repo change, no cross-repo concerns.
Dependencies
No blocking dependencies found on the board. This ticket is in backlog alongside other backlog items. The ticket references the ror-css-guide repo as a CSS conventions reference -- the agent will need to read that repo during implementation. No board items are blocked by this ticket, and this ticket is not blocked by any in-progress items.
Acceptance Criteria
9 acceptance criteria, all verifiable:
- [x] "Zero Tailwind dependencies" -- grep-verifiable
- [x] "All colors use var(--token)" -- grep-verifiable (no hardcoded hex outside :root)
- [x] "CSS comments mark component boundaries" -- grep-verifiable
- [x] "Mobile-first layout at 390px, desktop breakpoint at 600px" -- visually verifiable
- [x] "Production Docker build serves styled pages" -- build-verifiable
- [x] "Flash messages styled for notice and alert" -- visually verifiable
- [x] "Landing page has hero, feature cards, and contact form" -- visually verifiable
- [x] "Login page renders the Keycloak sign-in button" -- visually verifiable
- [x] "Dashboard page renders styled" -- visually verifiable
All criteria are testable. The Docker build command is provided in Test Expectations.
Blast Radius
Low. This is a single Rails app with no downstream CSS consumers. No sibling services share these stylesheets. The layout and view changes are self-contained. The only external reference is the ror-css-guide conventions repo, which is read-only input.
Decomposition Assessment
10 file targets in 1 repo, 9 acceptance criteria. This technically exceeds the 5-minute rule thresholds (>5 AC). However, the changes are tightly coupled -- removing Tailwind and replacing with semantic CSS is a single atomic operation. Decomposing would create artificial boundaries (e.g., "remove Tailwind" vs "add CSS" would conflict on every file). The work is mechanical: strip utility classes, write semantic classes, build one CSS file. Estimated agent time: 5-10 minutes. No decomposition recommended despite the size, given the tight coupling.
Recommendations
- [SCOPE] Create architecture note
arch-rails-appfor the Rails application component. Thearch:rails-applabel is used on 9 board items but has no backing architecture note in pal-e-docs.
-
Review: NetworkPolicy: allow pal-enterprises -> Postgres + Keycloak
review-1183-2026-05-09Verdict: 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 required sections present per
template-issue-feature.Traceability
- [x] story:sso-gateway label — SSO gateway story
- [x] story note verified — found in project-pal-enterprises user-stories section (row: sso-gateway, role: Authenticated user, metric: Single login grants access to all platform tools)
- [x] arch:rails-app label — Rails app architecture component
- [ ] arch note MISSING — [SCOPE] Create architecture note arch-rails-app for component rails-app. Note: this ticket actually modifies Terraform in pal-e-platform, not the Rails app itself. Consider whether arch:platform-infra would be more accurate.
- [x] Forgejo issue — ldraney/pal-e-platform#357, open
File Targets
- [x]
terraform/network-policies.tf— verified: file exists (10KB), Postgres allowlist at lines 158-180 (netpol_postgres), Keycloak allowlist at lines 134-156 (netpol_keycloak). Pattern is clear: addfromentry withnamespaceSelectormatchingpal-enterprises. No existing reference topal-enterprisesin the file.
Repo Placement
Correct. Issue filed on
ldraney/pal-e-platform, file target isterraform/network-policies.tfin that repo.Dependencies
No items in
in_progressornext_upon board-pal-enterprises. Related infra items on the board:- #1184 — Kustomize overlay (backlog, independent)
- #1185 — Woodpecker CI pipeline (backlog, independent)
Constraint documented: must be merged before first prod deploy. No blocking dependencies.
Acceptance Criteria
3 criteria, all verifiable by an agent:
pal-enterprisesnamespace in Postgres NetworkPolicy — verifiable by reading diffpal-enterprisesnamespace in Keycloak NetworkPolicy — verifiable by reading difftofu plan -lock=falseshows clean diff — valid command, but requires cluster access. Agent can verify the HCL syntax is correct; live plan requires infra credentials.
Blast Radius
Additive-only change. Existing pattern shows other services already in both allowlists (e.g.,
basketball-apiin both Postgres and Keycloak,westside-ai-assistantin Keycloak). No downstream consumers affected. No risk of breaking existing policies.Decomposition Assessment
1 file target, 1 repo, 3 acceptance criteria. Estimated agent time well under 5 minutes. No decomposition needed.
Recommendation
[SCOPE]Create architecture notearch-rails-appfor the Rails app component. This note is referenced by thearch:rails-applabel on this and 8 other board items but does not exist in pal-e-docs.
-
Review: Scaffold Rails app with landing page, contact form, and Keycloak SSO dashboard
review-1176-2026-05-08Verdict: 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 required sections for a Feature issue are present and well-structured.
Traceability
- [x] story:sso-gateway label — verified in project-pal-enterprises user-stories table (Role: Authenticated user, Metric: Single login grants access to all platform tools)
- [x] story:landing-page label — verified in project-pal-enterprises user-stories table (Role: Visitor, Metric: Can understand services offered and submit contact form)
- [ ] arch:rails-app label — arch note MISSING — [SCOPE] Create architecture note
arch-rails-appfor component rails-app - [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/forgejo_admin/pal-enterprises/issues/1, state: open
- [ ] sop-keycloak-client-creation — referenced in Context and Constraints but does not exist in pal-e-docs — [SCOPE] Create SOP note
sop-keycloak-client-creation
File Targets
This is a greenfield scaffold — all 14 file targets are new files to create. No existing files to verify against. File paths are well-structured and follow Rails conventions. The explicit "Files NOT relevant" section (no migrations) is a good scope boundary.
- [x] Gemfile — standard Rails dependency file
- [x] config/database.yml — proper for CNPG connection
- [x] config/environments/development.rb — Rails convention
- [x] config/initializers/omniauth.rb — correct location for OmniAuth config
- [x] config/routes.rb — standard Rails routing
- [x] app/controllers/pages_controller.rb — valid path
- [x] app/controllers/contacts_controller.rb — valid path
- [x] app/controllers/sessions_controller.rb — valid path
- [x] app/controllers/dashboard_controller.rb — valid path
- [x] app/views/pages/home.html.erb — valid path
- [x] app/views/contacts/new.html.erb — valid path
- [x] app/views/dashboard/index.html.erb — valid path
- [x] k8s/dev.yaml — follows westside-ror pattern
- [x] app/mailers/contact_mailer.rb — valid path
Repo Placement
OK — issue is filed on forgejo_admin/pal-enterprises and all work targets the same repo. No cross-repo concerns.
Dependencies
- Keycloak
pal-erealm must exist (it does, per Context) - New OIDC client
pal-enterprisesmust be registered — depends onsop-keycloak-client-creationwhich does not exist yet - CNPG cluster must allow connections from user
ldraney - Postgres namespace NetworkPolicy must be updated to allow pal-enterprises
- No blocking items on the board — only one other item (repo placeholder #1177)
- References
westside-rork8s pattern — should verify that repo/pattern is accessible to implementing agent
Acceptance Criteria
10 acceptance criteria. Each is testable by an agent in principle:
- AC 1-3: Landing page and contact form — verifiable via request specs or system tests
- AC 4-5: Keycloak flow — requires mock or real Keycloak; integration test mentioned in Test Expectations covers this
- AC 6-7: Dashboard access and sign-out — verifiable via controller tests
- AC 8: Auth guard — covered by unit test expectation
- AC 9: k8s deployment — requires cluster access, not automatable in unit tests but verifiable post-deploy
- AC 10: Health check — verifiable via request spec
Criteria are well-written and specific. However, 10 AC across 14 files is too much for a single pass.
Blast Radius
pal-e-hub— this replaces it. Decommission plan not mentioned but acceptable for a greenfield ticket (decommission would be a separate ticket)- Postgres NetworkPolicy change could affect other services if done incorrectly — bounded risk
- Keycloak client registration is a platform-wide SSO concern — if misconfigured, could affect existing pal-e-hub flows
Decomposition Assessment
NEEDS DECOMPOSITION — This ticket violates all three thresholds of the 5-minute rule:
- 14 file targets (threshold: >3)
- 10 acceptance criteria (threshold: >5)
- Estimated agent work: 20-30 minutes across Rails scaffold, Keycloak integration, mailer setup, k8s deployment, and tests
Suggested decomposition:
- Phase 1: Rails scaffold + landing page — Gemfile, routes, pages_controller, home view, health check, basic k8s/dev.yaml
- Phase 2: Contact form + mailer — contacts_controller, contact view, contact_mailer, email delivery
- Phase 3: Keycloak SSO integration — omniauth initializer, sessions_controller, auth callback, session management
- Phase 4: Authenticated dashboard — dashboard_controller, dashboard view, auth guard, sign-out
Recommendations
- [SCOPE] Create architecture note
arch-rails-appfor component rails-app - [SCOPE] Create SOP note
sop-keycloak-client-creation(referenced in issue body but does not exist) - [DECOMPOSE] 14 files, 10 AC — route to
skill-decompose-ticketfor automated sub-ticket creation
Project Page 1
-
pal-enterprises
project-pal-enterprisesVision
The front door to the pal-e platform. A single Rails app that serves two audiences: visitors see a landing page with services overview and a contact form; authenticated users land on a dashboard linking every platform tool. This replaces pal-e-hub and becomes the SSO entry point — log in once here, and you're logged into everything.
User Stories
Key Story Note Role Success Metric landing-page TBD Visitor Can understand services offered and submit contact form sso-gateway TBD Authenticated user Single login grants access to all platform tools tool-dashboard TBD Authenticated user Dashboard shows all tools with live status client-portal TBD Client Client can view their project board and active work via read-only agency link Architecture
Architecture notes TBD:
- Domain Model —
arch-domain-pal-enterprises - Data Flow —
arch-dataflow-pal-enterprises - Deployment —
arch-deployment-pal-enterprises
Key decisions:
- Keycloak OIDC via
omniauth-openid-connectgem,pal-erealm - No local user table — Keycloak is the auth source of truth
- Contact form is stateless — email delivery only, no DB storage
- Replaces pal-e-hub (SvelteKit) with Rails-native auth flow
Board
See
board-pal-enterprisesStatus
Bootstrapping. Repo created on Forgejo. README committed. Issue #1 filed. No code yet.
Milestones
None yet.
Repos
Repo Platform Role Status pal-enterprises Forgejo Application Bootstrapping - Domain Model —
Convention 1
-
Convention: Client Project Structure
convention-client-project-structureConvention: Client Project Structure
Every client on the pal-e platform gets four surfaces. pal-enterprises itself dogfoods the same pattern.
Rule
Every client gets four surfaces: (1) a login on
pal-enterpriseswith a role-aware dashboard, and three URLs linked from that dashboard: (2)[business]-docs— a separate RoR repo rendering project documentation, user stories, and mermaid diagrams (plain CSS via ror-css-guide, no Tailwind), (3)[business]-agency— read-only access to their pal-e-docs project page showing kanban, tickets, and Forgejo issues, and (4)[business]-app— a RoR monolith serving the public landing page, business owner admin view, and customer dashboard. Docs are built FIRST — they are the contract between Lucas, AI, and the client. Agency comes second. App comes third.Rationale
Docs-first means alignment before code. Before any parallel work begins, Lucas, AI agents, and the client reach shared understanding of user stories, system architecture, and project scope through the docs app. The agency surface gives the client ongoing visibility into work in progress without needing to learn developer tools. The app is a monolith because Rails makes role-gated views natural — admin and customer experiences in one repo, not separate deployments. pal-enterprises follows the identical pattern one level up: it IS the SSO gateway and dashboard for Lucas's own business, the same way each client's surfaces serve theirs.
Examples
Correct Incorrect Why Build westside-docsfirst as a separate RoR repo with plain CSS. Align on user stories and diagrams before coding the app.Jump straight into building the Westside app and add docs later. Docs are the contract. Without alignment, parallel work drifts. westside-agencylinks to the Westside project page in pal-e-docs. Marcus sees kanban and tickets read-only.Build a custom admin dashboard for Marcus to track project progress. pal-e-docs already has the project page and kanban. No need to rebuild it — just give scoped access. westside-appis one RoR repo. Marcus logs in → admin view. Parents log in → their dashboard. Public visitors → landing page.Separate westside-adminandwestside-appdeployments.Rails monolith with role-gated views. One repo, one deployment, multiple experiences. Dogfooding — pal-enterprises:
Surface pal-enterprises (Lucas) Client (e.g., Westside / Marcus) SSO gateway pal-enterprises.app— prospective clients land here, submit contact form, book appointmentClient logs into pal-enterprises, sees their dashboard docs pal-enterprises-docs— platform vision, architecture, what pal-enterprises will bewestside-docs— program docs, user stories, diagramsagency pal-e-agency— pal-enterprises project page in pal-e-docswestside-agency— Westside project page in pal-e-docsapp pal-enterprises.appitself (the SSO gateway IS the app)westside-app— public landing + admin + customer viewsContact form (pal-enterprises): Mandatory fields are business name, owner's name, logo, and email. No self-serve registration — submission redirects to a calendar page to book an appointment with Lucas. Accounts are provisioned only after the appointment.
Docs app conventions:
- Separate RoR repo per client — fresh Rails project
- No Tailwind — uses
~/ror-css-guide(plain CSS, design tokens, mobile-first) - Renders markdown + mermaid diagrams
- Behind Keycloak auth via pal-enterprises SSO
- Client has access — this is the shared understanding, not internal docs
Kanban philosophy: A kanban session begins and ends the same day. All its tickets complete in one focused session. This avoids the overhead of context re-startup. Kanban boards in pal-e-docs are focused work sessions, not open-ended backlogs.
Enforcement
SOP-enforced —
service-onboarding-sopincludes the four-surface deployment as part of client provisioning. Docs-first build order is convention only — depends on discipline.Related
service-onboarding-sop— the procedure that provisions surfacesproject-pal-enterprises— the platform front door (dogfooding instance)project-westside-basketball— reference client implementationconvention-kustomize-overlay— how each surface's deployment overlay is structured
Board 1
-
pal-enterprises
board-pal-enterprisesNo content