paldocs

paldocs forgejo

Notes

Doc 13
  • Verdict: PASS

    Ticket

    ldraney/pal-e-platform#492 — ConfigMap + volume mount for paldocs Keycloak login theme. Adds kubernetes_config_map_v1.keycloak_paldocs_theme to terraform, mounts theme files at /opt/keycloak/themes/paldocs/login in the Keycloak pod.

    Environment

    Production k3s cluster on archbox. Namespace: keycloak. Pod: keycloak-7bdc69975f-lfzts.

    Checks

    # Criterion How Verified Result Evidence
    1 keycloak/themes/paldocs/login/theme.properties exists with parent=keycloak ls repo file + cat content PASS File exists (60 bytes). Content: parent=keycloak / import=common/keycloak / styles=css/login.css
    2 keycloak/themes/paldocs/login/resources/css/login.css exists with paldocs design tokens ls repo file PASS File exists (8.5k). Contains CSS variables: --color-bg, --color-surface, --color-accent (#2563eb), form fields, submit button, mobile-first responsive styles.
    3 kubernetes_config_map_v1.keycloak_paldocs_theme resource added to main.tf grep terraform/modules/keycloak/main.tf PASS Resource defined with file() function for theme.properties and login.css. Namespace references kubernetes_namespace_v1.keycloak.
    4 Volume mount at /opt/keycloak/themes/paldocs/login added to Keycloak deployment (read-only) kubectl get pod -o jsonpath volumeMounts PASS paldocs-theme /opt/keycloak/themes/paldocs/login readOnly=true
    5 Volume definition referencing the paldocs ConfigMap added to Keycloak pod spec kubectl get pod -o jsonpath volumes PASS Volume paldocs-theme references ConfigMap keycloak-paldocs-theme with items mapping: theme.properties and login.css -> resources/css/login.css
    6 tofu plan shows clean addition (no destroy/recreate of existing themes) kubectl get configmap + pod verification; subsequent CI pipelines succeed PASS ConfigMap keycloak-paldocs-theme exists in namespace keycloak (uid: 2d74a685). All three theme ConfigMaps (westside, landscaping, paldocs) coexist. Local tofu plan blocked by custom provider, but production state confirms clean apply.
    7 Keycloak pod restarts successfully with the new volume mount kubectl get pods + kubectl describe pod PASS Pod Running, 0 restarts, 13d uptime. Conditions: PodReadyToStartContainers=True, Initialized=True, Ready=True, ContainersReady=True.
    8 kubectl get configmap keycloak-paldocs-theme -n keycloak returns the theme files kubectl get configmap -o yaml PASS ConfigMap contains two data keys: theme.properties (parent=keycloak) and login.css (full CSS with design tokens).
    9 Theme files visible inside Keycloak pod at mount path kubectl exec cat/ls inside pod PASS /opt/keycloak/themes/paldocs/login/theme.properties contains expected content. /opt/keycloak/themes/paldocs/login/resources/css/login.css exists.

    Regression Check

    Verified existing themes are unaffected:

    • ConfigMap keycloak-westside-theme still exists in keycloak namespace.
    • ConfigMap keycloak-landscaping-theme still exists in keycloak namespace.
    • Both westside and landscaping theme.properties files still mounted inside the pod at their expected paths.
    • All three theme volume mounts present and read-only.

    Tiers Executed

    • Tier 1 (Local): Verified theme files in repo, terraform resource definitions in main.tf. Local tofu plan blocked by custom GoDaddy provider not installed on this machine.
    • Tier 3 (Production): kubectl ConfigMap verification, pod health, volume mount inspection, in-pod file verification, regression checks on existing themes.

    Discovered Issues

    None. The paldocs theme ConfigMap and volume mount are correctly deployed following the established pattern (westside/landscaping). Note: the theme is not yet active on any realm — that requires the downstream ticket (pal-e-services#162: Set login_theme on pal-e-docs realm to paldocs), which is tracked as board item #1719 in the todo column.

  • Verdict: PASS

    Ticket

    Board item #1387 on board-paldocs — "Add paldocs Keycloak OIDC client via Terraform"
    Forgejo issue: ldraney/paldocs#26 (404 — work done in pal-e-services)
    Merged PR: ldraney/pal-e-services#163 "Add paldocs Keycloak OIDC client to pal-e-docs realm"
    Merge commit: b80c829
    Shipped: A Keycloak OIDC client (paldocs) in the pal-e-docs realm, provisioned via Terraform in pal-e-services.

    Environment

    Production k3s cluster. Keycloak namespace (keycloak-7bdc69975f-lfzts, Running, 0 restarts). Terraform state via pal-e-services/terraform with k3s.tfvars.

    Checks

    # Criterion How Verified Result Evidence
    1 Keycloak OIDC client for paldocs exists in pal-e-docs realm Keycloak Admin API: GET /admin/realms/pal-e-docs/clients?clientId=paldocs PASS Client returned with ID 772cbc3d-b732-437a-9052-06c07dcfe71e, clientId=paldocs, enabled=true, protocol=openid-connect
    2 Client configured correctly for Rails OIDC integration Keycloak Admin API response field inspection PASS CONFIDENTIAL (publicClient=false), standardFlowEnabled=true (Auth Code), directAccessGrantsEnabled=false, PKCE S256, use_refresh_tokens=true. Redirect URIs: https://paldocs.app/auth/keycloak/callback + https://paldocs.tail5b443a.ts.net/auth/keycloak/callback (OmniAuth convention). Web origins: both domains. Realm-roles protocol mapper active (id_token + access_token + userinfo). backchannel_logout_session_required=true.
    3 tofu apply was successful (no drift) tofu plan -lock=false -var-file=k3s.tfvars PASS Zero paldocs-related changes in plan output. All paldocs resources (keycloak_openid_client, realm_role_protocol_mapper, realm, namespace, harbor, argocd app, funnel) refreshed successfully with no drift. Plan summary: "0 to add" for paldocs resources. 6 unrelated changes (ISS, westside-basketball, mdview, gcal-scheduler).

    Regression Check

    • pal-e-docs realm: enabled=true, displayName="Pal-E Docs", all 8 clients healthy (account, account-console, admin-cli, broker, pal-e-app, paldocs, realm-management, security-admin-console)
    • OIDC discovery: https://keycloak.tail5b443a.ts.net/realms/pal-e-docs/.well-known/openid-configuration returns valid config with authorization_code grant type
    • Keycloak pod: Running, 0 restarts, 13d uptime
    • Paldocs app: Pod running (paldocs-8585d75c88-ch7p5), root URL returns HTTP 200 on both Tailscale and paldocs.app domains
    • Terraform state: No unexpected drift on any Keycloak resources

    Discovered Issues

    None. The /auth/keycloak route returns 404 on the paldocs app, which is expected — the Rails OmniAuth integration is a separate ticket from the Keycloak client provisioning validated here.

  • Verdict: PASS

    Ticket

    ldraney/pal-e-platform#491 — Add paldocs namespace to Keycloak NetworkPolicy ingress allowlist. Merged via PR #493.

    Board item #1708 on board-paldocs: [sprint:3] NetworkPolicy: paldocs→Keycloak

    Environment

    Production cluster (archbox), keycloak namespace. Keycloak service: ClusterIP 10.43.87.39:80/TCP. Paldocs pod: paldocs-8585d75c88-ch7p5 (10.42.0.196).

    Checks

    # Criterion How Verified Result Evidence
    1 paldocs namespace added to Keycloak NetworkPolicy ingress rules kubectl get networkpolicy -n keycloak -o yaml PASS NetworkPolicy default-deny-ingress includes namespaceSelector matchLabels kubernetes.io/metadata.name: paldocs. Full allowlist: tailscale, westside-ai-assistant, pal-enterprises, landscaping-assistant, paldocs, monitoring, woodpecker, prediction-assistant, westside-basketball, intelligentstaffingsystems (10 namespaces).
    2 tofu apply succeeds Verified resource exists in cluster; subsequent pal-e-platform CI pipelines on main are green (pipelines #1505, #1504, #1501, #1499 all success) PASS NetworkPolicy resource is live with generation 15. Multiple successful CI pipelines have run on main since PR #493 merged, confirming no state drift.
    3 paldocs pod can reach keycloak.keycloak.svc.cluster.local:80 kubectl exec -n paldocs paldocs-8585d75c88-ch7p5 -- curl PASS Root URL (/) returns HTTP 302 (Keycloak redirect). OIDC discovery (/realms/master/.well-known/openid-configuration) returns HTTP 200. Connectivity fully confirmed.

    Regression Check

    • Keycloak pod healthy: 1/1 Running, 0 restarts, 13 days uptime.
    • landscaping-assistant can still reach keycloak (HTTP 302 on root) -- existing NetworkPolicy rules intact.
    • All 10 namespaces present in NetworkPolicy allowlist -- no entries were dropped when paldocs was added.
    • Paldocs pod healthy: 1/1 Running, 0 restarts.

    Discovered Issues

    The issue's test expectation stated curl keycloak.keycloak.svc.cluster.local/health should return 200. In practice, Keycloak's health probe is on management port 9000 (/health/ready), not the HTTP port 80. On port 80, /health returns 404. The correct connectivity verification on port 80 is the root URL (302) or the OIDC discovery endpoint (200). This is a minor inaccuracy in the test expectation, not a functional defect. No follow-up issue required.

  • Verdict: PASS

    Ticket

    ldraney/pal-e-platform#490 — DNS A record + Caddy reverse proxy block for paldocs.app, making the Rails app publicly accessible via HTTPS.

    Merged via PR #494: "feat: add DNS A record and Caddy site for paldocs.app"

    Board item: #1703 on board-paldocs

    Environment

    Production — public internet via Hetzner edge VPS (178.156.129.142). Caddy reverse proxy on edge VPS forwarding to paldocs.tail5b443a.ts.net (Tailscale). GoDaddy DNS A record managed by OpenTofu in pal-e-platform.

    Checks

    # Criterion How Verified Result Evidence
    1 godaddy_dns_record.paldocs_a resource added to dns.tf DNS resolution + PR #494 merged PASS getent hosts paldocs.app returns 178.156.129.142 — same IP as palinks.app and landscaping-assistant.app (Hetzner edge VPS)
    2 tofu apply succeeds and DNS propagates getent hosts paldocs.app PASS DNS resolves globally to 178.156.129.142. Record is live and propagated. (Cannot run tofu plan -lock=false from validation env — no terraform workspace access — but DNS resolution proves apply succeeded.)
    3 Caddy block added on edge VPS for paldocs.app → paldocs.tail5b443a.ts.net curl -sI https://paldocs.app PASS Response header via: 2.0 Caddy confirms Caddy is proxying. HTTP/2 200 returned with Rails session cookie (_pal_e_docs_rails_session).
    4 curl https://paldocs.app returns 200 with paldocs Rails app curl -s -o /dev/null -w "%{http_code}" https://paldocs.app PASS HTTP 200. HTML body contains <title>Projects — pal-e-docs</title> with full Rails asset pipeline (Turbo, Stimulus, application CSS). Sub-route /projects also returns 200.
    5 TLS certificate auto-provisioned by Caddy curl -v https://paldocs.app TLS output PASS TLSv1.3 / TLS_AES_128_GCM_SHA256. Subject: CN=paldocs.app. Issuer: Let's Encrypt (YE1). Expires: Oct 5, 2026. SSL verify result: 0 (valid).

    Regression Check

    Other domains on the same Hetzner edge VPS still healthy:

    • palinks.app: resolves to 178.156.129.142, HTTPS returns HTTP 200, valid TLS
    • landscaping-assistant.app: resolves to 178.156.129.142, HTTPS returns HTTP 302 (auth redirect — expected), valid TLS

    No regressions detected. Adding the paldocs.app Caddy block did not affect existing sites.

    Limitations

    Could not execute tofu plan -lock=false to verify zero drift — no terraform workspace access from validation environment. DNS resolution to the correct Hetzner edge IP is accepted as equivalent evidence that the GoDaddy DNS record was successfully applied via OpenTofu.

    Discovered Issues

    None.

  • Verdict: PASS

    Ticket

    ldraney/paldocs#50 — Sprint schema: migrations + models for sprint tracking. Merged via PR #71.

    Re-validation: First attempt failed because CI pipeline #146 broke at bundle-install (Bundler 4.x mismatch). Fixed by base-images PR #7 (Bundler 4.x upgrade) and paldocs PR #73/#74 (lockfile fixes). Pipeline #153 passes all steps. The sprint schema code was already deployed to production via the image built from commit 5075e35 (PR #73), which includes commit 25dcc57 (PR #71).

    Environment

    Production cluster, namespace paldocs. Pod: paldocs-6795474677-q6kgx (1/1 Running, 1 restart). Image: harbor.tail5b443a.ts.net/paldocs/app:5075e35. URL: https://paldocs.tail5b443a.ts.net.

    Checks

    # Criterion How Verified Result Evidence
    1 sprints table exists with name, status, timestamps, check constraint rails runner: Sprint.count, Sprint.column_names, attempted invalid status insert PASS Sprint.count=0, columns=[id, name, status, created_at, updated_at]. Invalid status rejected by model validation ("is not included in the list").
    2 sprint_projects join table with FK + unique index rails runner: SprintProject.count, attempted duplicate insert, attempted invalid FK PASS SprintProject.count=0, columns=[id, sprint_id, project_id, created_at, updated_at]. Duplicate (sprint_id, project_id) rejected by ActiveRecord::RecordInvalid. Invalid sprint_id rejected by ActiveRecord::RecordInvalid.
    3 board_items.sprint_id FK column added rails runner: BoardItem.column_names.include?('sprint_id'), attempted invalid FK PASS sprint_id present in BoardItem columns. Invalid sprint_id=999999 rejected by ActiveRecord::InvalidForeignKey.
    4 Sprint model with STATUSES, validations, scopes, associations rails runner: Sprint::STATUSES, Sprint.table_name, model associations PASS STATUSES=["planning", "active", "complete"]. table_name="sprints". has_many :sprint_projects, has_many :projects through, has_many :board_items.
    5 SprintProject model with belongs_to + uniqueness validation rails runner: SprintProject.table_name, uniqueness validation test PASS table_name="sprint_projects". Duplicate rejected.
    6 CI schema updated (db/ci_schema.sql) git show HEAD:db/ci_schema.sql grep sprint PASS ci_schema.sql contains CREATE TABLE IF NOT EXISTS for sprints, sprint_projects, and board_items with sprint_id column.
    7 RSpec tests pass Woodpecker pipeline #153 test step PASS 119 examples, 0 failures. Sprint-specific tests visible: "BoardItem sprint association" (4 examples).
    8 CI pipeline green Woodpecker pipeline #153 (main push) PASS All CI steps passed: clone, bundle-install, lint, test, audit. build-and-push completing at time of validation.
    9 Production deployment live kubectl get pods -n paldocs; image tag verification PASS Pod Running 1/1. Image tag 5075e35 includes sprint schema commit 25dcc57 (verified via git merge-base --is-ancestor).
    10 All routes healthy (frontend SOP) curl HTTP status for /, /up, /projects, /projects/paldocs, /boards/board-paldocs, /api/notes/search PASS All 6 routes returned HTTP 200.

    Regression Check

    Existing data verified intact: 63 projects, 1987 notes, 1406 board items, 146 tags, 36781 blocks, 43 repos. All route-level smoke tests pass. Board view renders correctly. Projects index page renders correctly with 63 projects. No new errors observed.

    Discovered Issues

    None.

  • Verdict: FAIL

    Ticket

    paldocs#50 — Sprint schema: migrations + models for sprint tracking. Merged via PR #71 (merge commit 25dcc57). Board item #1560 on board-paldocs.

    Environment

    Production cluster, namespace paldocs. Running image: harbor.tail5b443a.ts.net/paldocs/app:311f4a4d00a76d8b95525c75a5eebe64024e4d32 (commit 311f4a4, from PR #66 — NOT the sprint schema commit).

    Tier Determination

    Labels: arch:rails-db → API repo checklist (Tier 1 local + Tier 3 prod). Tier 1 blocked by same Bundler corruption as CI. Tier 3 executed.

    Checks

    # Criterion How Verified Result Evidence
    1 Sprint.create persists with correct defaults Rails runner in prod pod: table_exists?('sprints') FAIL Returns false — table does not exist in production database
    2 sprint.projects returns selected projects Rails runner: table_exists?('sprint_projects') FAIL Returns false — join table does not exist
    3 BoardItem.where(sprint: s) scopes correctly Rails runner: column_exists?('board_items', 'sprint_id') FAIL Returns false — column not added to board_items
    4 sprint.board_items returns only tagged items Depends on sprint_id column existing FAIL Blocked by check #3
    5 Sprint status scopes (planning/active/complete) Depends on sprints table existing FAIL Blocked by check #1

    CI Pipeline

    Pipeline #146 (merge commit push): FAILURE. Step bundle-install exits 20 — Bundler 2.6.9 in CI image cannot read lockfile generated by Bundler 4.0.11. Additionally, Gemfile.lock:471 has a corrupted SHA256 for the rexml gem (extra characters). The build-and-push step was skipped, so no new Docker image was produced.

    This is a pre-existing CI issue — pipelines #130 (embedding PR #65), #141 (docs PR #70), and #146 (sprint PR #71) all fail identically at bundle-install. The Bundler version mismatch predates the sprint schema work.

    Root Cause

    The CI Docker image bundles Bundler 2.6.9, but the repo's Gemfile.lock was generated with Bundler 4.0.11 (introduced in PR #69, the bundle-update PR). The lockfile also contains a corrupt SHA256 hash at line 471 (rexml entry has trailing 142 characters). Combined, these prevent bundle install from succeeding in CI, which blocks image builds and therefore blocks deployment.

    Pod Health (Regression Check)

    Current pod paldocs-79495cc784-f5l5x is Running (1/1 ready, 1 restart in 36min). Health endpoint /up returns HTTP 200. Existing functionality is unaffected — the sprint schema PR added new tables and model files only, with no changes to existing code.

    Discovered Issues

    • CI Bundler version mismatch — Gemfile.lock requires Bundler 4.0.11 but CI image has 2.6.9. Needs either CI image update or lockfile regeneration with compatible Bundler. This blocks ALL deployments, not just this ticket.
    • Corrupt Gemfile.lock SHA256 — Line 471 (rexml gem) has malformed hash. Needs lockfile regeneration.

    Code Review (Static Analysis)

    Despite the deployment failure, the code itself is correct:

    • 3 migrations: create_sprints (with check constraint), create_sprint_projects (with unique index), add_sprint_id_to_board_items (nullable FK)
    • Sprint model: STATUSES constant, validations, 3 status scopes, has_many through associations, dependent:nullify on board_items
    • SprintProject model: belongs_to associations, uniqueness validation
    • BoardItem model: optional sprint association added
    • Project model: has_many :sprint_projects + :sprints through added
    • ci_schema.sql updated with all 3 new tables
    • 3 spec files with comprehensive coverage (CRUD, validations, scopes, associations, bidirectional access, destroy behavior)
    • No wave_number (removed per sprint philosophy — correct)
    • No controller/view changes (correct — UI is paldocs#56)
  • Verdict: PASS

    Ticket

    ldraney/paldocs#67 (board item #1571 on board-paldocs) — Update docs/ to reflect Rails schema ownership (post-consolidation) and create docs/sprint-philosophy.md codifying sprints as flat parallel batches.

    Merged PR: ldraney/paldocs#70

    Environment

    Production: paldocs.tail5b443a.ts.net (Tailscale funnel). Repo content verified via git clone of main branch post-merge.

    Checks

    # Criterion How Verified Result Evidence
    1 All internal doc links in README resolve (6 docs) Parsed README for (docs/*.md) links, checked each file exists on disk PASS 6/6 files exist: architecture.md, data-model.md, infrastructure.md, user-stories.md, auth-visibility-architecture.md, sprint-philosophy.md
    2 No broken cross-references between docs Extracted markdown links from all docs/*.md files, verified targets exist PASS infrastructure.md -> data-model.md (exists), architecture.md -> data-model.md (exists)
    3 Zero stale references to Alembic, pal-e-api, or FastAPI in docs grep -rin "alembic\|pal-e-api\|fastapi" README.md docs/ PASS Zero matches
    4 shared-database.md deleted Checked file existence on disk PASS File does not exist in docs/
    5 sprint-philosophy.md created with correct content Verified file exists, 50 lines, opens with expected heading and definitions PASS File present, covers: definitions, core rules, DORA alignment, waves vs sprints, label convention
    6 README no longer references shared-database.md grep "shared-database" README.md PASS Zero matches
    7 All doc files reflect Rails as sole schema owner Verified key sections: data-model.md "Schema Ownership", architecture.md "Rails Role", infrastructure.md "Database Migration", auth-visibility "Context" PASS All sections correctly state Rails is sole migration authority; no Alembic/FastAPI/pal-e-api references

    Route-Level Smoke Test

    Route HTTP Status Result
    GET / 200 PASS
    GET /projects/paldocs 200 PASS
    GET /boards/board-paldocs 200 PASS
    GET /notes/project-paldocs 200 PASS
    GET /up (health check) 200 PASS

    Pipeline Status

    Woodpecker pipeline #141 (merge commit for PR #70): FAILURE at bundle-install step. Root cause: Bundler version mismatch (CI image has Bundler 2.6.9, Gemfile.lock generated with Bundler 4.0.11). This is a pre-existing CI infrastructure issue unrelated to PR #70's docs-only changes. The update-kustomize-tag step succeeded, and the deployment is live and healthy. Pipeline #146 (subsequent PR #71 merge) shows the same failure pattern, confirming this is systemic.

    Regression Check

    All 5 critical routes from architecture.md return HTTP 200. Visual check via screenshot confirms the project listing page renders correctly. No regressions detected — this was a docs-only change with zero runtime impact.

    Discovered Issues

    CI pipeline is broken due to Bundler version mismatch (Bundler 2.6.9 in CI image vs 4.0.11 in Gemfile.lock). This affects all paldocs pipelines, not just PR #70. This is a pre-existing issue and should be tracked separately.

    Validation Tier

    Tier 3 only (content verification) — arch:docs label, docs-only change with no runtime impact.

  • Validation Result: PASS

    Issue: paldocs#49 — Schema ownership transfer

    PR: paldocs#60 — feat: transfer database schema ownership to Rails

    QA Review: APPROVED (agent a9108894c8fd7ff46)

    What was validated

    • pg_dump of production schema captured as db/structure.sql (841 lines, all tables/indexes/triggers/FK constraints)
    • config.active_record.schema_format switched from :ruby to :sql
    • neighbor gem added for pgvector vector(2560) column on blocks
    • First Rails migration: add status VARCHAR(50) to projects — proves migration authority
    • 6 rspec tests passing (3 scope tests, 1 default value, 2 column property tests)
    • ci_schema.sql updated for CI compatibility (vanilla postgres:17, no pgvector)
    • No regressions — 21 pre-existing request spec failures identical on main
    • Migration is purely additive — pal-e-docs backward compatibility preserved

    Nits (non-blocking)

    • by_status scope could benefit from enum validation for self-documentation
    • structure.sql intentionally omits new migration version — load pattern worth documenting
    • Migration spec typing minor
    • No automated CI schema drift detection yet

    CI Note

    Nokogiri bumped 1.19.3 → 1.19.4 (6 CVEs) in same PR branch before merge. Resolved bundler-audit failures across all branches.

  • Overview

    MCP (Model Context Protocol) server for paldocs — thin JSON-RPC over stdio proxy that maps MCP tool calls to Rails API endpoints.

    Architecture

    • Protocol: JSON-RPC over stdio (language-agnostic)
    • Server: New "paldocs" server replacing "pal-e-docs" (pal-e-mcp)
    • Transport: Tool call → HTTP request to Rails API → JSON response
    • Registration: ~/.mcp.json entry

    Tool Surface

    36 tools across 7 groups: Notes (12), Boards (6), Board Items (6), Projects (3), Repos (3), Tags (1), Search (2). Tool names use mcp__paldocs__* prefix.

    Transition Strategy

    1. New "paldocs" server runs alongside old "pal-e-docs"
    2. Verify all 36 tools return matching results
    3. Migrate tool references in claude-custom (hooks, agents, skills, settings)
    4. Remove old server from ~/.mcp.json, archive pal-e-mcp repo
    • arch-rails-api — the API endpoints this server calls
    • arch-rails-views — separate concern (UI, not MCP)
  • User Story: Project Pages story-paldocs-project-pages

    User Story: Project Pages

    User stories for the paldocs project pages feature area.

    Stories

    • As a superadmin, I want to browse projects and view their notes, repos, and boards so that I can quickly navigate the knowledge base
    • As a superadmin, I want to read note content inline so I don't have to open a separate tab
    • As a superadmin, I want sections collapsed by default showing only recent items so I can find what's active

    Scope

    • Project index with card grid (done)
    • Project show with notes/repos lists (done)
    • Render page note blocks inline (#12)
    • Sort by recent activity via Postgres trigger (#13)
  • Architecture: Rails Hotwire/Stimulus Patterns

    Conventions for Hotwire (Turbo + Stimulus) usage in the paldocs Rails app.

    Stimulus Controllers

    • Controllers live in app/javascript/controllers/
    • Registered via importmap (no webpack/esbuild build step)
    • Example: kanban_controller.js for drag-and-drop board interactions

    Turbo Frames

    • Turbo Frames used for partial page updates without full reloads
    • Each frame scoped to a resource section (e.g. notes list, board column)
    • Use data-turbo-permanent on elements that must persist state across Turbo navigation

    Progressive Enhancement Pattern

    • HTML <details>/<summary> provides a no-JS baseline for collapsible sections
    • Stimulus controllers layer on enhancement (animation, async loading) when JS is available
    • Pattern ensures the app remains functional without JavaScript
  • User Story: Auth story-paldocs-auth

    User Story: Auth

    Stories

    • Superadmin — As a superadmin, I want docs gated by role so that sensitive content isn't publicly accessible.
    • Member — As a member, I want to browse docs relevant to my role without seeing admin-only content.

    Scope

    Keycloak-based authentication and visibility-based access control for paldocs. Notes should be filterable by visibility level (public, member, admin) with Keycloak roles determining what a user can see.

    • ldraney/paldocs#23 — spike: Keycloak auth + visibility-based access control
    • arch-keycloak-paldocs — architecture note (pending spike deliverable)
    • arch-keycloak — platform-wide Keycloak architecture (pal-enterprises)
  • User Story: Kanban story-paldocs-kanban

    User Story: Kanban

    As a project manager, I want an interactive kanban board with drag-and-drop columns, phase grouping, and label visibility so that I can track and organize work visually.

    Scope

    • Drag-and-drop card movement between columns (done)
    • Phase grouping within columns (planned - #11)
    • Label/badge rendering on cards (done)
Review 70
  • Verdict: READY

    Re-review of board item #1718. Previous review (review-1718-2026-07-03) returned NEEDS_REFINEMENT because arch:keycloak backing note was not found. The note exists as arch-keycloak-paldocs (named to avoid collision with pal-enterprises' arch-keycloak). Traceability is now confirmed complete.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- "Dependency of ldraney/paldocs #82"
    • [x] Repo -- ldraney/pal-e-platform
    • [x] User Story -- full As a/I want/So that format
    • [x] Context -- explains 3-repo pattern, references landscaping PR #416 and westside
    • [x] File Targets -- lists files to create, modify, and NOT touch
    • [x] Feature Flag -- "none" (correct, infra-only change)
    • [x] Acceptance Criteria -- 7 criteria
    • [x] Test Expectations -- 3 test items + run command
    • [x] Constraints -- 4 constraints including pattern reference and read_only flag
    • [x] Checklist -- present
    • [x] Related -- 5 items linked

    All sections from template-issue-feature are present and complete.

    Traceability

    • [x] story:auth label -- role-based access control via Keycloak
    • [x] story note verified -- story-paldocs-auth found in project-paldocs user-stories section
    • [x] arch:keycloak label -- Keycloak infrastructure
    • [x] arch note verified -- arch-keycloak-paldocs note exists in pal-e-docs (paldocs project, architecture type, active status). Named with -paldocs suffix to avoid collision with pal-enterprises' arch-keycloak note.
    • [x] Forgejo issue -- pal-e-platform #492, state: open

    File Targets

    • [x] keycloak/themes/paldocs/login/theme.properties -- TO CREATE. Parent directory keycloak/themes/ exists with landscaping/ and westside/ siblings. Landscaping reference file confirms expected content: parent=keycloak, import=common/keycloak, styles=css/login.css.
    • [x] keycloak/themes/paldocs/login/resources/css/login.css -- TO CREATE. CSS content originates from paldocs #82 (documented dependency).
    • [x] terraform/modules/keycloak/main.tf -- EXISTS. Verified at full path. Contains both reference implementations (westside lines 29-39, landscaping lines 45-55).

    Line references in the issue are accurate:

    • [x] "lines 42-55" -- landscaping ConfigMap resource (actual: comment at 41, resource at 45-55). Close enough for agent guidance.
    • [x] "lines 178-182" -- landscaping volume_mount block. Verified exact match.
    • [x] "lines 249-265" -- landscaping volume definition block. Verified exact match.

    Repo Placement

    Correct. Issue filed on pal-e-platform, all file targets are in pal-e-platform. The 3-repo pattern is clearly documented: paldocs (CSS source), pal-e-platform (this ticket -- ConfigMap/volume), pal-e-services (realm setting). Each repo has its own Forgejo issue.

    Dependencies

    • Upstream: paldocs #82 (board item #1705, column: todo, 3pts) -- CSS theme source. Issue explicitly states "must be completed first." This ordering must be respected during sprint execution.
    • Downstream: pal-e-services #162 (board item #1719, column: backlog, 1pt) -- sets login_theme = "paldocs" on the realm. Cannot apply until this ticket and #82 are complete.
    • Sibling sprint:3 auth items: paldocs #26 (OIDC client TF, todo), pal-e-platform #491 (NetworkPolicy, todo), pal-e-deployments #224 (env vars, todo). Independent of this ticket but part of the same auth story.

    Dependencies are documented in the issue body and clear.

    Acceptance Criteria

    All 7 criteria are agent-verifiable:

    • File existence checks (criteria 1-2): trivial
    • Terraform resource presence (criteria 3-5): grep/read verification
    • tofu plan clean addition (criterion 6): real command provided in Test Expectations
    • Keycloak pod restart (criterion 7): requires apply, appropriate for validation phase

    Test commands are real and executable. No missing criteria identified.

    Blast Radius

    Low. The change is purely additive:

    • New ConfigMap resource -- no effect on existing westside/landscaping ConfigMaps
    • New volume + volume_mount on Keycloak deployment -- Kubernetes will restart the pod, causing brief downtime for all Keycloak-authenticated apps (acceptable; probes handle recovery)
    • Issue explicitly states "Files the agent should NOT touch: Existing westside/landscaping theme resources"
    • AC #6 verifies: "tofu plan shows clean addition (no destroy/recreate of existing themes)"

    Decomposition Assessment

    No decomposition needed.

    • 3 file targets, all in 1 repo -- under threshold
    • 7 acceptance criteria -- slightly above 5, but all are trivial verification steps following an established copy-paste pattern
    • Estimated agent time: well under 5 minutes -- the landscaping implementation is a direct template to follow

    Recommendation

    No action needed. Ticket is ready for agent execution.

    Note: Sprint execution must respect the dependency ordering -- paldocs #82 (CSS source) must complete before this ticket starts, since the agent needs the CSS content to write login.css.

  • Verdict: READY

    Re-review. Previous verdict was NEEDS_REFINEMENT due to arch:keycloak note not found. The note exists as arch-keycloak-paldocs (named to avoid collision with pal-enterprises arch-keycloak note). Traceability now confirmed.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Dependency of ldraney/paldocs #82
    • [x] Repo -- ldraney/pal-e-services
    • [x] User Story -- present
    • [x] Context -- present, references existing theme patterns
    • [x] File Targets -- present, includes both modify and NOT touch lists
    • [x] Feature Flag -- none (appropriate for a config change)
    • [x] Acceptance Criteria -- 3 criteria
    • [x] Test Expectations -- present with run command
    • [x] Constraints -- present, documents execution ordering
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:auth label -- maps to story-paldocs-auth
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:keycloak label -- maps to arch-keycloak-paldocs (paldocs-specific slug to avoid collision with pal-enterprises arch-keycloak note)
    • [x] arch note verified -- arch-keycloak-paldocs note exists in pal-e-docs, note_type=architecture, status=active, project=paldocs
    • [x] Forgejo issue -- pal-e-services #162, open

    File Targets

    • [x] terraform/k3s.tfvars -- verified: pal-e-docs realm at lines 66-72, no existing login_theme, symlinks to ~/secrets/pal-e-services/k3s.tfvars
    • [x] terraform/keycloak.tf -- verified: line 35 already has login_theme = each.value.login_theme (correctly marked NOT touch)
    • [x] terraform/variables.tf -- verified: line 113 has login_theme = optional(string) (correctly marked NOT touch)

    Repo Placement

    OK -- issue filed on ldraney/pal-e-services, file targets are in pal-e-services/terraform/. Single repo, correct placement.

    Dependencies

    • Blocked by #1718 (pal-e-platform #492) -- ConfigMap + volume mount must be deployed first so the theme directory exists in the Keycloak pod. Documented in Constraints section. Currently in backlog.
    • Consumed by #1705 (paldocs #82) -- Keycloak login theme CSS source in paldocs repo. Documented in Lineage. Currently in todo.
    • Related to #1387 (paldocs #26) -- Keycloak OIDC client for same realm, not a blocker for this ticket.

    Acceptance Criteria

    3 criteria, all agent-verifiable:

    • [x] Grep for login_theme in k3s.tfvars -- verifiable
    • [x] tofu plan showing single attribute change -- verifiable with provided run command
    • [x] Visual verification of login page -- requires manual check or screenshot, reasonable for a 1-pointer

    Blast Radius

    Minimal. One-line tfvars change following an established pattern. Two other realms already use login_theme (westside at line 50, landscaping at line 85). The keycloak_realm resource and variable definition already support the field. No downstream consumers affected by this change alone.

    Decomposition Assessment

    No decomposition needed. 1 file target, 3 acceptance criteria, estimated agent work well under 1 minute. This is a single-line config change.

    Recommendation

    No action needed. Scope is solid, all traceability verified, file targets confirmed, fits comfortably in a single agent pass.

  • Verdict: APPROVED

    Re-review of board item #1388 (ldraney/paldocs#27). Previous review (review-1388-2026-07-03) returned NEEDS_REFINEMENT due to missing arch-rails-db backing note. That note has since been created with full content. All previous findings resolved.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Related to paldocs #23 (auth spike)
    • [x] Repo -- ldraney/pal-e-api (Alembic migration) + ldraney/paldocs (ci_schema.sql)
    • [x] User Story
    • [x] Context -- References auth spike decision doc
    • [x] File Targets -- 3 in pal-e-api, 1 in paldocs (cross-repo, separate PR)
    • [x] Feature Flag -- none (correct, internal schema change)
    • [x] Acceptance Criteria -- 6 items
    • [x] Test Expectations -- unit tests + pytest command
    • [x] Constraints -- 4 constraints documented
    • [x] Checklist
    • [x] Related -- project + dependency chain

    Traceability

    • [x] story:auth label -- story-paldocs-auth ("role-based access control via Keycloak")
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:rails-db label -- shared database architecture component
    • [x] arch note verified -- arch-rails-db exists: "Database: paldocs (Shared CNPG, Schema Ownership, Dual Migrations)". Full content covers diagram, components table, key decisions on schema ownership, dual-migration rules, and cross-cutting deploy ordering. Active status, paldocs project.
    • [x] Forgejo issue -- ldraney/paldocs#27, state: open

    File Targets

    • [x] src/pal_e_docs/models.py -- verified: Note.is_public at line 66, Project.is_public at line 119. No visibility field yet (correct, to be added).
    • [x] src/pal_e_docs/schemas.py -- verified: is_public in 9 schema locations across ProjectCreate, ProjectUpdate, ProjectOut, NoteCreate, NoteUpdate, NoteOut, NoteSummary, NoteSearchResult. No visibility field yet.
    • [x] alembic/versions/ -- directory exists with existing migrations. New migration file will be created here.
    • [x] db/ci_schema.sql (paldocs) -- verified: file exists, 2 is_public references for notes and projects tables. No visibility column yet.

    Repo Placement

    OK. Issue is filed under ldraney/paldocs but the issue body explicitly states both repos. Primary work (Alembic migration, models, schemas) is in ldraney/pal-e-api. Cross-repo work (ci_schema.sql) is in ldraney/paldocs as a separate PR. Filing under paldocs is acceptable -- this is a paldocs auth feature tracked on board-paldocs. The arch:rails-db label is an established convention for shared database schema work on this board.

    Dependencies

    Correctly documented in issue body:

    • Depends on: nothing (wave 1 -- confirmed, no blocking items on the board)
    • Blocks: paldocs #29 (Role-based visibility filtering in controllers) -- board item #1390, confirmed in backlog with sprint:3 label
    • Related: paldocs #28 (Rails OIDC) -- same sprint:3/story:auth, independent track (no dependency)

    Acceptance Criteria

    6 criteria, all agent-verifiable:

    • AC 1-2: Column existence -- verifiable via migration + introspection
    • AC 3: Backfill logic -- verifiable via unit test (is_public mapping)
    • AC 4-5: Model/schema updates -- verifiable by inspection and test run
    • AC 6: Existing tests pass -- verifiable via PALDOCS_DATABASE_PATH=:memory: pytest

    Test command is valid and matches the project's test convention.

    Blast Radius

    • is_public is referenced in 28 locations across route modules in pal-e-api
    • paldocs Rails app has 0 is_public references in app/ (clean)
    • Ticket correctly constrains scope: "Do not drop is_public yet" and "Files the agent should NOT touch: src/pal_e_docs/routes/"
    • Route-level changes are deferred to paldocs #29 (visibility filtering) -- good decomposition

    Decomposition Assessment

    3 file targets in pal-e-api + 1 in paldocs (cross-repo, separate PR). 6 acceptance criteria (borderline at >5 threshold). However, all AC are part of one atomic migration -- column additions, backfill, and model/schema updates are tightly coupled. Decomposing would create coordination overhead without benefit. Cross-repo ci_schema.sql update is trivial. No decomposition needed.

    Recommendation

    No action needed. All previous NEEDS_REFINEMENT findings have been resolved. The arch-rails-db note now exists with comprehensive content covering the shared CNPG cluster, schema ownership model, and dual-migration conventions. The ticket is well-scoped, fully traceable, and ready for agent execution.

  • Verdict: READY

    Re-review of board item #1705 (paldocs #82). Previous review review-1705-2026-07-03 returned NEEDS_REFINEMENT for two scope gaps. Both are now resolved.

    Previous Findings -- Resolution

    • [SCOPE] arch-keycloak note missing -- FIXED. arch-keycloak-paldocs note created (ID 1899, note_type architecture, status active, project paldocs). Comprehensive note covering OIDC client, themes, secrets wiring, key decisions. References paldocs #82 directly in its Related section. Platform-level arch-keycloak note also exists in pal-enterprises.
    • [SCOPE] Cross-repo deployment issues missing -- FIXED. Two Forgejo issues created and tracked on board-paldocs:
      • pal-e-platform #492 (board #1718, 2pt, backlog, sprint:3) -- ConfigMap keycloak-paldocs-theme + volume mount in keycloak module
      • pal-e-services #162 (board #1719, 1pt, backlog, sprint:3) -- login_theme = "paldocs" on pal-e-docs realm in k3s.tfvars

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Related to paldocs #28
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present, well-formed
    • [x] Context -- thorough, references landscaping-assistant pattern
    • [x] File Targets -- 2 files in paldocs, cross-repo dependencies documented with issue references
    • [x] Feature Flag -- none (correct for CSS-only)
    • [x] Acceptance Criteria -- 4 items
    • [x] Test Expectations -- manual only (correct for CSS-only)
    • [x] Constraints -- present, clear boundaries
    • [x] Checklist -- present
    • [x] Related -- present, includes cross-repo issue references

    Traceability

    • [x] story:auth label -- story-paldocs-auth
    • [x] story note verified -- story-paldocs-auth exists (ID 1897, project paldocs, tags: active, user-story). References arch-keycloak-paldocs.
    • [x] arch:keycloak label -- present on board item
    • [x] arch note verified -- arch-keycloak-paldocs exists (paldocs project, architecture type, active). Platform-level arch-keycloak also exists (pal-enterprises). Both reference this issue.
    • [x] Forgejo issue -- ldraney/paldocs#82, open

    File Targets

    • [x] keycloak-theme/paldocs/login/theme.properties -- new file (expected). Reference pattern verified at ~/landscaping-assistant/keycloak-theme/landscaping/login/theme.properties (parent=keycloak, import=common/keycloak, styles=css/login.css).
    • [x] keycloak-theme/paldocs/login/resources/css/login.css -- new file (expected). Reference CSS at landscaping-assistant is 341 lines of PatternFly overrides.
    • [x] app/assets/stylesheets/application.css -- exists (1212 lines). Design token source for CSS extraction.
    • [x] Cross-repo: pal-e-platform ConfigMap pattern verified -- existing themes (westside, landscaping) use kubernetes_config_map_v1 resource + volume mount in modules/keycloak/main.tf. Now tracked by #492.
    • [x] Cross-repo: pal-e-services pal-e-docs realm exists, no login_theme set yet. Now tracked by #162.

    Repo Placement

    OK. Forgejo issue filed on ldraney/paldocs. Primary file targets (CSS theme source) are in paldocs. Cross-repo deployment work properly separated into dedicated issues in the correct repos (pal-e-platform #492, pal-e-services #162).

    Dependencies

    • paldocs #26 (board #1387) -- Keycloak OIDC client via Terraform -- backlog, sprint:3. Theme cannot be tested without a Keycloak realm/client. Code work is independent but deployment is blocked.
    • paldocs #28 (board #1389) -- Rails OIDC integration with Keycloak -- backlog, sprint:3. Lineage reference. Logically independent.
    • pal-e-platform #492 (board #1718) -- ConfigMap + volume mount -- backlog, sprint:3. Deploy dependency. NEW -- addresses previous review gap.
    • pal-e-services #162 (board #1719) -- login_theme realm config -- backlog, sprint:3. Deploy dependency. NEW -- addresses previous review gap.
    • pal-e-deployments #224 (board #1704) -- KEYCLOAK env vars -- todo, sprint:3. Related auth infra.
    • pal-e-platform #491 (board #1708) -- NetworkPolicy paldocs to Keycloak -- backlog, sprint:3. Network dependency for auth flow.

    All dependencies documented in issue body and tracked on board.

    Acceptance Criteria

    • [x] AC1: "keycloak-theme/paldocs/login/ directory created" -- agent-verifiable via file existence check
    • [x] AC2: "CSS matches paldocs design tokens" -- partially agent-verifiable (grep application.css for tokens, confirm they appear in login.css)
    • [x] AC3: "No Keycloak branding visible" -- manual only, requires deployed theme rendering
    • [x] AC4: "Theme renders correctly on mobile" -- manual only, requires deployed theme rendering

    4 acceptance criteria, 2 agent-verifiable, 2 manual-only. Reasonable for CSS-only feature.

    Blast Radius

    Low. CSS-only changes isolated to Keycloak login page. No app code impact. No shared service changes. Cross-repo ConfigMap/volume mount risk belongs to pal-e-platform #492, not this ticket.

    Decomposition Assessment

    No decomposition needed. 2 file targets in 1 repo, 4 acceptance criteria, estimated agent work well under 5 minutes. CSS pattern well-established from landscaping-assistant reference.

    Recommendation

    No action needed. Both previous NEEDS_REFINEMENT issues are resolved. Ticket is ready for agent execution.

  • Verdict: NEEDS_REFINEMENT

    Board item #1718 on board-paldocs. Forgejo issue: ldraney/pal-e-platform#492. First review of a well-structured feature ticket that follows an established pattern (landscaping/westside themes). One traceability gap prevents READY.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- dependency of paldocs #82
    • [x] Repo -- ldraney/pal-e-platform
    • [x] User Story -- present, well-formed
    • [x] Context -- thorough, documents 3-repo pattern with prior art
    • [x] File Targets -- create and modify targets with "do not touch" section
    • [x] Feature Flag -- "none" (infra work, appropriate)
    • [x] Acceptance Criteria -- 7 items, all verifiable
    • [x] Test Expectations -- 3 items + run command
    • [x] Constraints -- 4 items referencing specific lines
    • [x] Checklist -- present
    • [x] Related -- 5 entries with cross-references

    Traceability

    • [x] story:auth label -- role-based access control via Keycloak
    • [x] story note verified -- found in project-paldocs user-stories section as story-paldocs-auth
    • [x] arch:keycloak label -- Keycloak infrastructure component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-keycloak for Keycloak component. Search for "arch-keycloak" returned no results in pal-e-docs.
    • [x] Forgejo issue -- ldraney/pal-e-platform#492, open

    File Targets

    • [x] keycloak/themes/paldocs/login/theme.properties -- to CREATE. Parent directory does not exist yet (expected for new theme). Reference implementation at keycloak/themes/landscaping/login/theme.properties confirmed with content: parent=keycloak, import=common/keycloak, styles=css/login.css
    • [x] keycloak/themes/paldocs/login/resources/css/login.css -- to CREATE. Reference at keycloak/themes/landscaping/login/resources/css/login.css confirmed with CSS design tokens.
    • [x] terraform/modules/keycloak/main.tf -- to MODIFY. File exists (292 lines). Line references verified:
      • Lines 42-55: landscaping ConfigMap resource (comment + resource block) -- confirmed
      • Lines 178-182: landscaping volume_mount block -- confirmed exact match
      • Lines 249-265: landscaping volume definition block -- confirmed exact match
    • [x] terraform/modules/keycloak/variables.tf -- correctly listed as "do not touch", file exists

    Repo Placement

    Correct. Issue filed on ldraney/pal-e-platform, all file targets are in pal-e-platform. The 3-repo pattern is properly scoped: paldocs #82 (CSS design in paldocs repo), this issue (ConfigMap/volume in pal-e-platform), pal-e-services #162 (realm config in pal-e-services). Each repo has its own issue.

    Dependencies

    • paldocs #82 (board item #1705, backlog) -- CSS source files. Issue states "must be completed first." This is a hard upstream dependency. The agent cannot create login.css with actual paldocs design tokens until paldocs #82 defines them.
    • pal-e-services #162 (board item #1719, backlog) -- downstream. Sets login_theme = "paldocs" on the realm. Depends on this issue completing first.
    • paldocs #26 (board item #1387, backlog) -- Keycloak OIDC client. Same story:auth chain but not a direct dependency of this issue.
    • Dependency chain documented in issue body. Ordering is clear: #82 -> #492 -> #162.

    Acceptance Criteria

    7 acceptance criteria. All are agent-verifiable:

    • AC 1-2: File existence checks (trivial)
    • AC 3-5: Code pattern checks in main.tf (grep/read)
    • AC 6: tofu plan verification (run command provided)
    • AC 7: Pod health check (kubectl)

    Test expectations include the exact run command: cd ~/pal-e-platform/terraform && tofu plan -var-file=k3s.tfvars. Solid.

    Blast Radius

    Low. This is an additive change following an established pattern with two working examples. No existing resources are modified. The Keycloak pod will restart when the new volume mount is applied, causing brief downtime for all Keycloak-served auth flows (westside, landscaping, pal-e-docs). This is expected and acceptable for a dev environment.

    Decomposition Assessment

    7 acceptance criteria exceeds the >5 threshold, but no decomposition needed. Rationale:

    • 3 file targets in 1 repo -- under the >3 files / >2 repos threshold
    • AC 1-5 are aspects of a single code change (add theme to TF module)
    • AC 6-7 are verification steps, not separate work items
    • Established pattern with two reference implementations -- agent work estimated at 2-3 minutes
    • Decomposition would create overhead without value

    Recommendations

    • [SCOPE] Create architecture note arch-keycloak for the Keycloak component. Multiple board items reference arch:keycloak (#1387, #1389, #1705, #1718, #1719) but no backing note documents the component's architecture, integration points, or theme system.
  • Verdict: APPROVED

    Re-review of board item #1387. Previous review (review-1387-2026-07-03) returned NEEDS_REFINEMENT with four recommendations. All four have been addressed. Scope is solid, file targets verified, traceability complete, fits in a single agent pass.

    Previous Findings -- Resolution Status

    • [x] [SCOPE] "Create architecture note arch-keycloak" -- FIXED. arch-keycloak-paldocs created (architecture type, active, paldocs project). Comprehensive content: mermaid sequence diagram, component table, key decisions (confidential client rationale, PKCE S256, refresh tokens, CSS-only theme, Terraform-managed). References landscaping-assistant as reference implementation. Platform-level arch-keycloak also exists in pal-enterprises.
    • [x] [BODY] "Update Constraints: change precedent from pal-enterprises to landscaping-assistant" -- FIXED. Context section now says "The landscaping-assistant client is the closest precedent -- same CONFIDENTIAL type, PKCE S256, realm roles mapper enabled, and critically it includes both Tailscale hostnames AND the public domain." Constraints section says "Follow the existing landscaping-assistant client pattern (dual-origin: Tailscale + public domain)."
    • [x] [BODY] "Add pal-e-platform #491 to Related" -- FIXED. Related section now includes both pal-e-platform#490 (DNS A record + Caddy) and pal-e-platform#491 (NetworkPolicy) with dependency descriptions.
    • [x] [BODY] "Repo placement advisory" -- Acknowledged (non-blocking). Issue remains on paldocs with ### Repo field set to ldraney/pal-e-services. The Repo field disambiguates for agents.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Child of paldocs#23 (spike: Keycloak auth + visibility-based access control)
    • [x] Repo -- ldraney/pal-e-services
    • [x] User Story -- As a platform operator, I want a Keycloak OIDC client provisioned for paldocs
    • [x] Context -- References landscaping-assistant dual-origin pattern, pal-e-docs realm, existing roles
    • [x] File Targets -- 1 modify target + 2 no-touch targets
    • [x] Feature Flag -- none
    • [x] Acceptance Criteria -- 7 items (expanded from 4 in previous review)
    • [x] Test Expectations -- tofu validate + tofu plan -var-file=k3s.tfvars
    • [x] Constraints -- 6 constraints including dual-origin pattern
    • [x] Checklist -- 3 items
    • [x] Related -- 5 items including both platform dependencies

    Traceability

    • [x] story:auth label -- present on board item #1387
    • [x] story note verified -- story-paldocs-auth exists in project paldocs, listed on project-paldocs user-stories section ("role-based access control via Keycloak")
    • [x] arch:keycloak label -- present on board item #1387
    • [x] arch note verified -- arch-keycloak-paldocs exists (architecture type, active status, paldocs project). Covers OIDC client, themes, secrets wiring, NetworkPolicy. Also arch-keycloak exists in pal-enterprises as platform-level reference.
    • [x] Forgejo issue -- ldraney/paldocs#26, open

    File Targets

    • [x] terraform/k3s.tfvars -- verified: exists (symlink to ~/secrets/pal-e-services/k3s.tfvars). keycloak_clients map present. No paldocs entry yet. landscaping-assistant precedent confirmed: public_client=false, standard_flow_enabled=true, direct_access_grants_enabled=false, pkce_code_challenge_method="S256", include_realm_roles_mapper=true, use_refresh_tokens=true, dual-origin URIs (Tailscale + landscaping-assistant.app).
    • [x] terraform/keycloak.tf -- verified: exists. keycloak_openid_client.this at line 148 uses for_each on var.keycloak_clients. Realm roles mapper at line 186. No changes needed -- correct.
    • [x] terraform/variables.tf -- verified: exists. Type definition at line 167-196 supports all fields needed: public_client (line 173), standard_flow_enabled (line 174), direct_access_grants_enabled (line 175), pkce_code_challenge_method (line 176), include_realm_roles_mapper (line 185), use_refresh_tokens (line 194). No changes needed -- correct.

    Repo Placement

    Advisory (non-blocking): issue filed on ldraney/paldocs, work is in ldraney/pal-e-services. The ### Repo field says ldraney/pal-e-services, which disambiguates for agents. Other cross-repo items on the paldocs board (pal-e-platform #490, #491, pal-e-deployments #224) are filed where the work happens, but this convention break is not blocking.

    Dependencies

    • Upstream: pal-e-platform #491 / board #1708 (NetworkPolicy: paldocs to Keycloak) -- on board, sprint:3, backlog. Now documented in Related section.
    • Upstream: pal-e-platform #490 / board #1703 (DNS A record + Caddy block for paldocs.app) -- on board, sprint:3, backlog. Now documented in Related. Public domain must resolve before public redirect URIs work, but client creation itself doesn't depend on this.
    • Downstream: #1704 (KEYCLOAK_* env vars), #1389 (Rails OIDC integration), #1390 (visibility filtering). All depend on this client existing.
    • Realm: pal-e-docs realm already exists with admin and user roles. No realm-level changes needed.

    Acceptance Criteria

    7 criteria, all agent-verifiable. Expanded from 4 in previous review to cover both domains:

    • [x] paldocs client exists in pal-e-docs realm -- verifiable via tofu plan output
    • [x] Realm roles mapper attached -- verifiable via tofu plan (mapper resource)
    • [x] Redirect URIs include both paldocs.tail5b443a.ts.net and paldocs.app -- verifiable via tfvars inspection
    • [x] Web origins include both domains -- verifiable via tfvars inspection
    • [x] Post-logout redirect URIs include both domains -- verifiable via tfvars inspection
    • [x] tofu plan shows only new client + mapper -- verifiable via plan output
    • [x] Client secret retrievable -- verifiable post-apply via Keycloak admin console

    Blast Radius

    Low. The for_each pattern in keycloak.tf isolates each client as a separate Terraform resource. Adding paldocs to keycloak_clients cannot affect existing clients. The pal-e-docs realm is shared with pal-e-app but no realm-level changes are needed (admin/user roles already defined).

    Decomposition Assessment

    1 file target, 7 AC, estimated agent work under 5 minutes. No decomposition needed.

    Recommendations

    No action needed.

  • Verdict: READY

    Board item #1708 -- [sprint:3] NetworkPolicy: paldocs->Keycloak
    Forgejo issue: ldraney/pal-e-platform#491
    Issue type: Feature
    Template: template-issue-feature
    Re-review of: review-1708-2026-07-03 (previous verdict: NEEDS_REFINEMENT -- missing arch-k8s note)

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- related to paldocs #26, hard dependency documented
    • [x] Repo -- ldraney/pal-e-platform
    • [x] User Story -- "As the paldocs Rails app I want network access to Keycloak..."
    • [x] Context -- explains default-deny ingress, current allowlist, landscaping-assistant precedent
    • [x] File Targets -- terraform/network-policies.tf identified, exclusions listed
    • [x] Feature Flag -- "none" (correct for infra change)
    • [x] Acceptance Criteria -- 3 items
    • [x] Test Expectations -- kubectl exec curl test + tofu plan verification
    • [x] Constraints -- references existing pattern and PR #413 precedent
    • [x] Checklist -- standard 3-item checklist
    • [x] Related -- project reference present

    All 12 template sections present. Complete.

    Traceability

    • [x] story:auth label -- story-paldocs-auth (role-based access control via Keycloak)
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:k8s label -- Kubernetes infrastructure
    • [x] arch note verified -- arch-k8s note exists (id: 2039, type: architecture, status: active, project: paldocs). Title: "Kubernetes: paldocs (k3s, Kustomize, NetworkPolicies, CNPG)". Contains diagram, components table, key decisions (including: "Default-deny network policies... Must be added to keycloak allowlist for auth"), and related links. Previous blocker resolved.
    • [x] Forgejo issue -- ldraney/pal-e-platform#491, state: open

    File Targets

    • [x] terraform/network-policies.tf -- verified: file exists at ~/pal-e-platform/terraform/network-policies.tf. Resource netpol_keycloak confirmed. Current ingress allowlist: tailscale, westside-ai-assistant, pal-enterprises, landscaping-assistant, monitoring. paldocs is indeed absent, confirming the ticket's description.
    • [x] Exclusion terraform/modules/keycloak/ -- verified: directory exists, exclusion is reasonable since Keycloak deployment is unchanged.

    Repo Placement

    Correct. The NetworkPolicy is defined in pal-e-platform/terraform/network-policies.tf and the Forgejo issue is filed on ldraney/pal-e-platform. Single-repo change, no cross-repo work needed.

    Dependencies

    • Downstream: Board item #1387 (paldocs #26 -- "Add paldocs Keycloak OIDC client via Terraform") depends on this. Without NetworkPolicy access, paldocs cannot reach Keycloak for token exchange. Dependency is documented in the Lineage section.
    • Downstream (transitive): Board items #1389 (paldocs #28 -- Rails OIDC integration) and #1390 (paldocs #29 -- Role-based visibility filtering) depend on #26, making them transitively dependent on this ticket.
    • Sibling: Board item #1704 (pal-e-deployments #224 -- Add KEYCLOAK_* env vars) is independent but part of the same auth story wave.
    • No blocking upstream dependencies -- this ticket can proceed independently.

    Acceptance Criteria

    3 ACs + 2 test expectations. All are agent-verifiable:

    • [x] AC1: "paldocs namespace added to Keycloak NetworkPolicy" -- verifiable by code review / grep
    • [x] AC2: "tofu apply succeeds" -- verifiable by running tofu apply (requires cluster access)
    • [x] AC3: "paldocs pod can reach keycloak.keycloak.svc.cluster.local:80" -- verifiable via kubectl exec (paldocs pod is deployed and running)
    • [x] Test 1: kubectl exec curl health check -- specific command provided, returns 200
    • [x] Test 2: tofu plan shows only NetworkPolicy change -- verifiable, prevents scope creep

    Criteria are specific, actionable, and testable. No missing criteria.

    Blast Radius

    • Low risk: Adding a namespace to an ingress allowlist is purely additive. No existing service connectivity is affected.
    • Related gap -- netpol_ollama: The paldocs namespace is still absent from netpol_ollama's ingress allowlist. Currently only pal-e-docs is allowed. If paldocs connects directly to Ollama for embeddings (board item #1562 -- Embedding pipeline is done), this would be blocked. This is a separate concern and does not affect this ticket.
    • Pattern consistency: The change follows the established pattern from PR #413 (landscaping-assistant). One-line addition to an existing list.

    Decomposition Assessment

    • 1 file target across 1 repo -- below threshold
    • 3 acceptance criteria -- below threshold
    • Estimated agent work: under 2 minutes (add one line to an existing ingress list)

    No decomposition needed.

    Recommendation

    No action needed. Previous blocker (missing arch-k8s note) has been resolved. The note now exists with comprehensive content covering the exact infrastructure this ticket modifies. Ticket is ready to move to todo.

  • Verdict: READY

    Re-review of board item #1703 — DNS A record + Caddy block for paldocs.app
    Forgejo issue: ldraney/pal-e-platform#490 (open)
    Type: Feature | Points: 3 | Sprint: 3
    Previous review: review-1703-2026-07-03 (NEEDS_REFINEMENT — missing backing notes)

    Previous NEEDS_REFINEMENT Resolution

    The prior review flagged two [SCOPE] issues. Both are now resolved:

    • story-paldocs-ios-testflight — created. note_type: user-story, status: active, project: paldocs. Includes role, acceptance criteria (6 items), success metric, critical path (4 ordered dependencies), sprint mapping, and related architecture references.
    • arch-infra — created. note_type: architecture, status: active, project: paldocs. Includes mermaid diagram (full traffic flow), components table (6 components), key decisions (4 items), and related note links.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — references paldocs #46
    • [x] Repo — ldraney/pal-e-platform
    • [x] User Story — well-formed (platform owner, public URL access)
    • [x] Context — good background, references existing pattern
    • [x] File Targets — terraform/dns.tf identified
    • [x] Feature Flag — none (correct for infra work)
    • [x] Acceptance Criteria — 5 items
    • [x] Test Expectations — 3 items
    • [x] Constraints — 3 items, references existing pattern
    • [x] Checklist — present
    • [x] Related — present

    Traceability

    • [x] story:ios-testflight label — present on board item
    • [x] story note verified — story-paldocs-ios-testflight exists (id 2374, note_type: user-story, status: active, project: paldocs). Full content with acceptance criteria, critical path, and sprint mapping.
    • [x] arch:infra label — present on board item
    • [x] arch note verified — arch-infra exists (id 2096, note_type: architecture, status: active, project: paldocs). Includes mermaid diagram, components table, and key decisions.
    • [x] Forgejo issue — ldraney/pal-e-platform#490, open

    Minor gap: project-paldocs user-stories section lists 5 stories (kanban, project-pages, auth, consolidation, sprint-orchestration) but does not yet include a link to story-paldocs-ios-testflight. The backing note exists with full content — this is a housekeeping update, not a scope blocker.

    File Targets

    • [x] terraform/dns.tf — verified: file exists at ~/pal-e-platform/terraform/dns.tf with 3 existing godaddy_dns_record resources (palinks_a, landscaping_assistant_a, westside_a) all using module.hetzner_edge.server_ipv4. Pattern is clear and well-established.
    • [x] terraform/modules/hetzner-edge/ — correctly marked as do-not-touch. Directory exists with 5 files (main.tf, variables.tf, outputs.tf, versions.tf, cloud-init.yaml). VPS already provisioned.

    Repo Placement

    Correct. Issue filed on pal-e-platform, file target (terraform/dns.tf) is in pal-e-platform. Caddy block is manual VPS config acknowledged in Constraints — not a repo file, so no repo mismatch. Single-repo scope.

    Dependencies

    • No blocking dependencies. This ticket can proceed independently.
    • This ticket IS a dependency for board item #1520 (paldocs #46 — Ship paldocs.app to TestFlight, sprint:4). Lineage correctly documents this relationship.
    • Also a prerequisite for auth tickets (#1387 paldocs #26 Keycloak client, #1389 paldocs #28 Rails OIDC) — public domain affects Keycloak redirect URIs. The arch-infra note documents this relationship.
    • No conflicts with other sprint:3 items (auth track items are independent until they need the public URL).

    Acceptance Criteria

    • AC1 (godaddy_dns_record resource) — agent-verifiable via code review
    • AC2 (tofu apply + DNS propagation) — manual, requires TF credentials and apply
    • AC3 (Caddy block on VPS) — manual, not in repo. Constraints correctly note this.
    • AC4 (curl https://paldocs.app returns 200) — manual end-to-end verification
    • AC5 (TLS auto-provisioned) — manual, Caddy handles automatically

    Only AC1 is agent-automatable. ACs 2-5 require manual execution. This is appropriate for an infra ticket — the agent writes the Terraform, the operator applies it and configures Caddy. Test Expectations correctly label items as "Manual".

    Blast Radius

    Low. Adding a 4th DNS A record to an established pattern. No changes to existing domains. No downstream consumers affected. Caddy reverse proxy addition is similarly isolated — new server block, no modification of existing blocks.

    Decomposition Assessment

    • 1 file target across 1 repo — no decomposition needed
    • 5 acceptance criteria but only 1 is a code change — within limits
    • Estimated agent work: less than 2 minutes (copy existing pattern, change domain name)
    • No decomposition needed

    Recommendation

    • [SCOPE] Add story-paldocs-ios-testflight link to project-paldocs user-stories section — the note exists but the project page list has not been updated to reference it. This is a minor housekeeping item and does not block the ticket.

    No action needed to move this ticket to todo. Scope is solid, traceability is complete (backing notes exist), file targets verified, single-repo single-file change following an established pattern.

  • Verdict: NEEDS_REFINEMENT

    Re-review after previous NEEDS_REVISION finding (missing NetworkPolicy dependency). That dependency is now filed as pal-e-platform #491 (board item #1708). Core scope is solid -- remaining findings are traceability gaps and a redirect URI scope concern.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Child of paldocs#23
    • [x] Repo -- ldraney/pal-e-services
    • [x] User Story
    • [x] Context
    • [x] File Targets
    • [x] Feature Flag -- none
    • [x] Acceptance Criteria -- 4 items
    • [x] Test Expectations -- tofu validate + plan
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    Traceability

    • [x] story:auth label -- present on board item #1387
    • [x] story note verified -- story-paldocs-auth found in project-paldocs user-stories section
    • [x] arch:keycloak label -- present on board item #1387
    • [ ] arch note MISSING -- search for "arch-keycloak" returned no results. [SCOPE] Create architecture note arch-keycloak for Keycloak component.
    • [x] Forgejo issue -- ldraney/paldocs#26, open

    File Targets

    • [x] terraform/k3s.tfvars -- verified: exists (symlink to ~/secrets/pal-e-services/k3s.tfvars). keycloak_clients map at line 92. No paldocs entry exists yet. pal-enterprises precedent confirmed at line 116 (public_client=false, PKCE S256, realm roles mapper, use_refresh_tokens).
    • [x] terraform/keycloak.tf -- verified: exists. Uses for_each on var.keycloak_clients (line 148-149). No changes needed.
    • [x] terraform/variables.tf -- verified: exists. Type supports public_client (line 173), pkce_code_challenge_method (line 176), include_realm_roles_mapper (line 185), use_refresh_tokens (line 194). No changes needed.

    Repo Placement

    Mismatch: issue filed on ldraney/paldocs but ### Repo says ldraney/pal-e-services. File targets are in pal-e-services/terraform/. Other cross-repo board items follow the convention of filing in the work repo: pal-e-platform #490, pal-e-deployments #224, pal-e-platform #491 are all filed where the work happens. This issue breaks that convention. Advisory -- the ### Repo field disambiguates for agents, so not blocking.

    Dependencies

    • Upstream (resolved): pal-e-platform #491 / board #1708 (NetworkPolicy: paldocs namespace to Keycloak) -- filed on board, sprint:3. This was the previous review finding, now addressed.
    • Downstream: #1704 (KEYCLOAK_* env vars in paldocs deployment), #1389 (Rails OIDC integration), #1390 (visibility filtering). These depend on the Keycloak client existing.
    • Not documented: NetworkPolicy dependency (#1708) is not mentioned in the issue body's Lineage or Related sections.

    Acceptance Criteria

    4 criteria, all agent-verifiable. Test expectations include real commands (tofu validate, tofu plan -var-file=k3s.tfvars). No missing criteria for the Terraform scope.

    Blast Radius

    Low. The for_each pattern in keycloak.tf isolates each client as a separate resource. Adding paldocs to keycloak_clients cannot affect existing clients. The pal-e-docs realm is shared with pal-e-app but no realm-level changes are needed (admin/user roles already exist).

    Redirect URI concern: The issue says "Follow the existing pal-enterprises client pattern exactly," but pal-enterprises only uses Tailscale hostnames. The landscaping-assistant client (line 139-166 in k3s.tfvars) is the better precedent -- it includes BOTH Tailscale hostname AND public domain URIs (landscaping-assistant.app). Since the user owns paldocs.app and DNS/Caddy work is on the board (#1703), the paldocs client should include paldocs.app URIs alongside paldocs.tail5b443a.ts.net. Following pal-enterprises literally would require a follow-up change when the public domain goes live.

    Decomposition Assessment

    1 file target, 4 AC, estimated agent work under 5 minutes. No decomposition needed.

    Recommendations

    • [SCOPE] Create architecture note arch-keycloak for Keycloak component in pal-e-docs.
    • [BODY] Update Constraints: change precedent from pal-enterprises to landscaping-assistant. Include both paldocs.tail5b443a.ts.net and paldocs.app in redirect URIs, web origins, and post-logout URIs. DNS/Caddy work (#1703) is on the board -- wiring both domains now avoids a follow-up client update.
    • [BODY] Add pal-e-platform #491 (NetworkPolicy) to Lineage or Related section as a dependency.
    • [BODY] Repo placement advisory: consider refiling as pal-e-services issue to match convention (other cross-repo items on paldocs board are filed where work happens). Not blocking -- ### Repo field disambiguates.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Related to paldocs #28
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present, well-formed
    • [x] Context -- thorough, references landscaping-assistant pattern
    • [x] File Targets -- 2 files in paldocs, cross-repo changes noted
    • [x] Feature Flag -- none (correct for CSS-only)
    • [x] Acceptance Criteria -- 4 items
    • [x] Test Expectations -- manual only (correct for CSS-only)
    • [x] Constraints -- present, clear boundaries
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:auth label -- story-paldocs-auth
    • [x] story note verified -- found in project-paldocs user-stories section: "role-based access control via Keycloak"
    • [x] arch:keycloak label -- present on board item
    • [ ] arch note MISSING -- search for "arch-keycloak" returned no results. Tracked by pal-e-platform #303 ("Create arch-keycloak note (platform traceability gap)") but not yet created. [SCOPE] Create architecture note arch-keycloak for the Keycloak component.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/82, open

    File Targets

    • [x] keycloak-theme/paldocs/login/theme.properties -- new file (expected). Pattern matches landscaping-assistant reference at ~/landscaping-assistant/keycloak-theme/landscaping/login/theme.properties (parent=keycloak, import=common/keycloak, styles=css/login.css).
    • [x] keycloak-theme/paldocs/login/resources/css/login.css -- new file (expected). Pattern matches landscaping reference (9.7KB CSS file with PatternFly overrides).
    • [x] app/assets/stylesheets/application.css -- exists (30KB). Design token source for CSS extraction. Agent can grep this for colors, fonts, spacing.
    • [x] Cross-repo: pal-e-platform ConfigMap pattern verified -- existing themes (westside, landscaping) use kubernetes_config_map_v1 resource + volume mount in modules/keycloak/main.tf. Theme files also stored at ~/pal-e-platform/keycloak/themes/{name}/login/ (identical copies for ConfigMap file() references).
    • [x] Cross-repo: pal-e-services realm config verified -- pal-e-docs realm in k3s.tfvars exists but has NO login_theme field yet. Needs login_theme = "paldocs" added.

    Repo Placement

    OK. Forgejo issue filed on ldraney/paldocs. Primary file targets (CSS theme source) are in paldocs. Cross-repo changes in pal-e-platform and pal-e-services are correctly identified as separate PRs.

    Note: The existing pattern stores theme files in BOTH the app repo (source of truth for co-evolution with application.css) AND pal-e-platform (for ConfigMap file() references). The issue correctly scopes paldocs as the primary target.

    Dependencies

    • paldocs #26 (board #1387) -- "Add paldocs Keycloak OIDC client via Terraform" -- backlog, sprint:3. Theme cannot be tested without a Keycloak realm/client. Code work is independent but deployment is blocked.
    • paldocs #28 (board #1389) -- "Rails OIDC integration with Keycloak" -- backlog, sprint:3. Lineage reference. Logically independent -- theme is CSS-only, no app code coupling.
    • Cross-repo deployment gap -- No Forgejo issues exist for: (1) pal-e-platform ConfigMap + volume mount for paldocs theme, (2) pal-e-services login_theme = "paldocs" on pal-e-docs realm. These steps are mentioned in the issue body as "separate PRs" but lack trackable tickets.

    Acceptance Criteria

    • [x] AC1: "keycloak-theme/paldocs/login/ directory created" -- agent-verifiable via file existence check
    • [x] AC2: "CSS matches paldocs design tokens" -- partially agent-verifiable (grep application.css for tokens, confirm they appear in login.css)
    • [x] AC3: "No Keycloak branding visible" -- manual only, requires deployed theme rendering
    • [x] AC4: "Theme renders correctly on mobile" -- manual only, requires deployed theme rendering

    4 acceptance criteria, 2 agent-verifiable, 2 manual-only. Reasonable for CSS-only feature. No automated test command (correct -- CSS theming has no unit test surface).

    Blast Radius

    Low. CSS-only changes isolated to Keycloak login page. No app code impact. No shared service changes. The only risk is in the cross-repo pal-e-platform ConfigMap/volume mount (separate PR) -- incorrect volume mounts could affect existing westside and landscaping themes. That risk belongs to the pal-e-platform PR, not this ticket.

    Decomposition Assessment

    No decomposition needed. 2 file targets in 1 repo, 4 acceptance criteria, estimated agent work well under 5 minutes. CSS pattern is well-established from landscaping-assistant reference.

    Recommendation

    • [SCOPE] Create architecture note arch-keycloak for the Keycloak component. Already tracked by pal-e-platform #303 but not yet done. Traceability gap -- all arch:keycloak board items reference a non-existent backing note.
    • [SCOPE] Create Forgejo issues for cross-repo deployment steps: (1) pal-e-platform issue for ConfigMap keycloak-paldocs-theme + volume mount + theme file copy, (2) pal-e-services issue for adding login_theme = "paldocs" to pal-e-docs realm in k3s.tfvars. Without these tickets, the deployment path is undocumented and untracked.
  • Verdict: NEEDS_REFINEMENT

    Re-review after previous NEEDS_REVISION (wrong repo). The issue body has been updated to correctly identify both repos and separate file targets. One remaining traceability gap: missing arch note.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Related to paldocs #23 (auth spike)
    • [x] Repo -- ldraney/pal-e-api (Alembic migration) + ldraney/paldocs (ci_schema.sql)
    • [x] User Story
    • [x] Context -- References auth spike decision doc
    • [x] File Targets -- 3 in pal-e-api, 1 in paldocs (cross-repo, separate PR)
    • [x] Feature Flag -- none (correct, internal schema change)
    • [x] Acceptance Criteria -- 6 items
    • [x] Test Expectations -- unit tests + pytest command
    • [x] Constraints -- 4 constraints documented
    • [x] Checklist
    • [x] Related -- project + dependency chain

    Traceability

    • [x] story:auth label present
    • [x] story note verified -- story-paldocs-auth exists in pal-e-docs and is listed in project-paldocs user-stories section
    • [x] arch:rails-db label present
    • [ ] arch note MISSING -- search for "arch-rails-db" returned empty. [SCOPE] Create architecture note arch-rails-db for the shared database layer (Alembic + Rails schema ownership)
    • [x] Forgejo issue -- ldraney/paldocs#27, state: open

    File Targets

    • [x] src/pal_e_docs/models.py -- verified: Note.is_public at line 119, Project.is_public at line 66. No visibility field yet (correct, to be added).
    • [x] src/pal_e_docs/schemas.py -- verified: is_public in ProjectCreate, ProjectUpdate, ProjectOut, NoteCreate, NoteUpdate, NoteOut, NoteSummary, NoteSearchResult. No visibility field yet.
    • [x] alembic/versions/ -- directory exists with 4 existing migrations. New migration file will be created here.
    • [x] db/ci_schema.sql (paldocs) -- verified: is_public BOOLEAN at lines 11 and 24 for projects and notes tables respectively. No visibility column yet.

    Repo Placement

    FIXED (was the primary issue in previous review). Issue is filed under ldraney/paldocs but the issue body now explicitly states: "Repo: ldraney/pal-e-api (Alembic migration) + ldraney/paldocs (ci_schema.sql update)". File targets are separated by repo with cross-repo work noted as "separate PR". Filing under paldocs is acceptable since this is a paldocs auth feature tracked on board-paldocs.

    Note: The arch:rails-db label was previously flagged as misleading (work is Alembic, not Rails). However, this label is an established convention on board-paldocs for shared database schema work -- used by 3 completed items including schema ownership transfer (#1559). The issue body is the agent spec, not the label, and the body is now explicit about Alembic.

    Dependencies

    Correctly documented in issue body:

    • Depends on: nothing (wave 1 -- confirmed, no blocking items on the board)
    • Blocks: paldocs #29 (Role-based visibility filtering in controllers) -- board item #1390, confirmed in backlog with sprint:3 label
    • Related: paldocs #28 (Rails OIDC) -- same sprint:3/story:auth, independent track (no dependency)

    Acceptance Criteria

    6 criteria, all agent-verifiable:

    • AC 1-2: Column existence -- verifiable via migration + introspection
    • AC 3: Backfill logic -- verifiable via unit test (is_public mapping)
    • AC 4-5: Model/schema updates -- verifiable by inspection and test run
    • AC 6: Existing tests pass -- verifiable via PALDOCS_DATABASE_PATH=:memory: pytest (correct command for pal-e-docs)

    Test command is valid and matches the project's test convention.

    Blast Radius

    • is_public is referenced in 20+ locations across 5 route modules (notes.py, projects.py, boards.py, blocks.py, links.py)
    • 67 test files reference is_public
    • paldocs Rails app has NO is_public references in app/ (clean -- Rails reads from shared DB but hasn't built visibility filtering yet)
    • Ticket correctly constrains scope: "Do not drop is_public yet" and "Files the agent should NOT touch: src/pal_e_docs/routes/"
    • Route-level changes are deferred to paldocs #29 (visibility filtering) -- good decomposition

    Decomposition Assessment

    3 file targets in pal-e-api + 1 in paldocs (cross-repo, separate PR). 6 acceptance criteria (borderline at >5 threshold). Estimated agent time: ~5 minutes. All AC are part of one atomic migration -- decomposing would be counterproductive. Cross-repo work is a trivial ci_schema.sql update. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails-db documenting the shared database layer: Alembic as schema owner, Rails as consumer, CNPG cluster, migration conventions. This note should cover the dual-ownership model established by schema ownership transfer (#1559).

    All other previous NEEDS_REVISION findings (wrong repo, missing repo clarity) have been resolved. The ticket is otherwise well-scoped and actionable.

  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Related to paldocs #26 and #28
    • [x] Repo -- ldraney/pal-e-deployments
    • [x] User Story -- As the paldocs Rails app / Keycloak connection details / OmniAuth OIDC
    • [x] Context -- References landscaping-assistant pattern, identifies 4 KEYCLOAK_* keys
    • [x] File Targets -- 1 modify target, 1 reference-only exclusion
    • [x] Feature Flag -- none (appropriate for infra wiring)
    • [x] Acceptance Criteria -- 4 items, all verifiable
    • [x] Test Expectations -- kubectl describe + kustomize build
    • [x] Constraints -- Pattern reference, manual secret step, dependency on #26
    • [x] Checklist -- Standard PR/tests/no-unrelated
    • [x] Related -- pal-e-docs project reference

    Traceability

    • [x] story:auth label -- role-based access control via Keycloak
    • [x] story note verified -- story-paldocs-auth exists in project paldocs, listed on project-paldocs user-stories section
    • [x] arch:k8s label -- Kubernetes (k3s) container orchestration
    • [x] arch note verified -- arch-k8s note exists (id: 2039, status: active)
    • [x] Forgejo issue -- ldraney/pal-e-deployments#224, state: open

    File Targets

    • [x] overlays/paldocs/prod/deployment-patch.yaml -- verified: file exists, currently has 3 env vars (RAILS_ENV, SECRET_KEY_BASE, PALDOCS_DATABASE_URL), no KEYCLOAK_* vars yet (expected)
    • [x] overlays/landscaping-assistant/prod/deployment-patch.yaml -- verified: reference pattern exists with KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET using secretKeyRef (lines 62-81 for initContainer, lines 134-153 for app container)

    Repo Placement

    Correct. Issue filed on pal-e-deployments, file targets are in pal-e-deployments. Single-repo change. No cross-repo work needed.

    Dependencies

    • Depends on paldocs #26 (Keycloak client via Terraform) -- board item #1387, backlog. Explicitly documented in Constraints section. Must complete first to provide the client secret value.
    • Related to paldocs #28 (Rails OIDC integration) -- board item #1389, backlog. Downstream consumer of these env vars.
    • NetworkPolicy ticket #1708 (pal-e-platform #491) -- sprint:3, backlog. Ensures paldocs pods can reach Keycloak. Does not block this ticket but both must be done for auth to function end-to-end.
    • Manual step: paldocs-secrets k8s secret must be updated with KEYCLOAK_* values after #26 provisions the client. Documented in Constraints.

    Acceptance Criteria

    4 criteria, all verifiable. First two (env vars added, correct secretKeyRef) are code-review-verifiable. Last two (pod restart, kubectl exec) require the paldocs-secrets secret to contain the KEYCLOAK_* keys, which depends on paldocs #26. This is properly documented in the Constraints section. Test expectation uses kustomize build -- note that standalone kustomize is not on PATH, but kubectl kustomize succeeds. Agent should use kubectl kustomize instead.

    Blast Radius

    Minimal. 4 other services already use this exact KEYCLOAK_* env var pattern (basketball-api, landscaping-assistant, westsidekingsandqueens, pal-enterprises). The pattern is proven and well-established across the platform. Adding env vars to a deployment patch has no downstream side effects -- the vars are only consumed by the Rails app's OmniAuth configuration.

    Decomposition Assessment

    No decomposition needed. 1 file target, 4 acceptance criteria, estimated agent work under 2 minutes. This is a copy-and-adapt from the landscaping-assistant pattern: add 4 env var blocks referencing paldocs-secrets instead of landscaping-assistant-secrets.

    Recommendation

    No action needed. Ticket is well-scoped and ready for execution.

  • Verdict: NEEDS_REFINEMENT

    Board item #1708 — [sprint:3] NetworkPolicy: paldocs→Keycloak
    Forgejo issue: ldraney/pal-e-platform#491
    Issue type: Feature
    Template: template-issue-feature

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — related to paldocs #26, hard dependency documented
    • [x] Repo — ldraney/pal-e-platform
    • [x] User Story — "As the paldocs Rails app I want network access to Keycloak..."
    • [x] Context — explains default-deny ingress, current allowlist, landscaping-assistant precedent
    • [x] File Targets — terraform/network-policies.tf identified, exclusions listed
    • [x] Feature Flag — "none" (correct for infra change)
    • [x] Acceptance Criteria — 3 items
    • [x] Test Expectations — kubectl exec curl test + tofu plan verification
    • [x] Constraints — references existing pattern and PR #413 precedent
    • [x] Checklist — standard 3-item checklist
    • [x] Related — project reference present

    All 12 template sections present. Complete.

    Traceability

    • [x] story:auth label — story-paldocs-auth (role-based access control via Keycloak)
    • [x] story note verified — found in project-paldocs user-stories section
    • [x] arch:k8s label — Kubernetes infrastructure
    • [ ] arch note MISSING — [SCOPE] Create architecture note arch-k8s for Kubernetes infrastructure component
    • [x] Forgejo issue — ldraney/pal-e-platform#491, state: open

    File Targets

    • [x] terraform/network-policies.tf — verified: file exists at ~/pal-e-platform/terraform/network-policies.tf. Resource netpol_keycloak confirmed at line 135. Current ingress allowlist: tailscale, westside-ai-assistant, pal-enterprises, landscaping-assistant, monitoring. paldocs is indeed absent, confirming the ticket's description.
    • [x] Exclusion terraform/modules/keycloak/ — reasonable, Keycloak deployment is unchanged by this work.

    Repo Placement

    Correct. The NetworkPolicy is defined in pal-e-platform/terraform/network-policies.tf and the Forgejo issue is filed on ldraney/pal-e-platform. Single-repo change, no cross-repo work needed.

    Dependencies

    • Downstream: Board item #1387 (paldocs #26 — "Add paldocs Keycloak OIDC client via Terraform") is blocked by this ticket. Without NetworkPolicy access, paldocs cannot reach Keycloak for token exchange. Dependency is documented in the Lineage section.
    • Downstream (transitive): Board items #1389 (paldocs #28 — Rails OIDC integration) and #1390 (paldocs #29 — Role-based visibility filtering) depend on #26, making them transitively dependent on this ticket.
    • Sibling: Board item #1704 (pal-e-deployments #224 — Add KEYCLOAK_* env vars) is independent but part of the same auth story wave.
    • No blocking upstream dependencies — this ticket can proceed independently.

    Acceptance Criteria

    3 ACs + 2 test expectations. All are agent-verifiable:

    • [x] AC1: "paldocs namespace added to Keycloak NetworkPolicy" — verifiable by code review / grep
    • [x] AC2: "tofu apply succeeds" — verifiable by running tofu apply (requires cluster access)
    • [x] AC3: "paldocs pod can reach keycloak.keycloak.svc.cluster.local:80" — verifiable via kubectl exec (requires running paldocs pod; paldocs is deployed and running per project state)
    • [x] Test 1: kubectl exec curl health check — specific command provided, returns 200
    • [x] Test 2: tofu plan shows only NetworkPolicy change — verifiable, prevents scope creep

    Criteria are specific, actionable, and testable. No missing criteria.

    Blast Radius

    • Low risk: Adding a namespace to an ingress allowlist is purely additive. No existing service connectivity is affected.
    • Related gap — netpol_ollama: The paldocs namespace is also absent from netpol_ollama's ingress allowlist (line 189). Currently only pal-e-docs is allowed. Board item #1562 (paldocs #52 — "Embedding pipeline — Solid Queue + Ollama + neighbor gem") is marked done, but if paldocs connects directly to Ollama, it would be blocked by the NetworkPolicy. This may warrant a separate ticket.
    • Pattern consistency: The change follows the established pattern from PR #413 (landscaping-assistant). One-line addition to an existing list.

    Decomposition Assessment

    • 1 file target across 1 repo — below threshold
    • 3 acceptance criteria — below threshold
    • Estimated agent work: under 2 minutes (add one line to an existing ingress list)

    No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-k8s for the Kubernetes infrastructure component. Multiple board items use the arch:k8s label (#1704, #1708) but no backing note exists in pal-e-docs.

    Once the arch note gap is addressed (or accepted as systemic), this ticket is ready to move to todo.

  • Verdict: NEEDS_REFINEMENT

    Board item #1703 — DNS A record + Caddy block for paldocs.app
    Forgejo issue: ldraney/pal-e-platform#490 (open)
    Type: Feature | Points: 3 | Sprint: 3

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — references paldocs #46
    • [x] Repo — ldraney/pal-e-platform
    • [x] User Story — well-formed (platform owner, public URL access)
    • [x] Context — good background, references existing pattern
    • [x] File Targets — terraform/dns.tf identified
    • [x] Feature Flag — none (correct for infra work)
    • [x] Acceptance Criteria — 5 items
    • [x] Test Expectations — 3 items
    • [x] Constraints — 3 items, references existing pattern
    • [x] Checklist — present
    • [x] Related — present

    Traceability

    • [x] story:ios-testflight label — present on board item
    • [ ] story note MISSING — project-paldocs user-stories section lists: kanban, project-pages, auth, consolidation, sprint-orchestration. No ios-testflight story entry. [SCOPE] Create user story entry on project-paldocs user-stories section for story:ios-testflight.
    • [x] arch:infra label — present on board item
    • [ ] arch note MISSING — search_notes("arch-infra") returned no results. [SCOPE] Create architecture note arch-infra for infrastructure component, or determine if arch:infra is foundational enough to not require a dedicated note.
    • [x] Forgejo issue — ldraney/pal-e-platform#490, open

    File Targets

    • [x] terraform/dns.tf — verified: file exists at ~/pal-e-platform/terraform/dns.tf with 3 existing godaddy_dns_record resources (palinks_a, landscaping_assistant_a, westside_a) all using module.hetzner_edge.server_ipv4. Pattern is clear and well-established.
    • [x] terraform/modules/hetzner-edge/ — correctly marked as do-not-touch. VPS already provisioned, confirmed via cloud-init.yaml showing Caddy install.

    Repo Placement

    Correct. Issue filed on pal-e-platform, file target (terraform/dns.tf) is in pal-e-platform. Caddy block is manual VPS config acknowledged in Constraints — not a repo file, so no repo mismatch. Single-repo scope.

    Dependencies

    • No blocking dependencies. This ticket can proceed independently.
    • This ticket IS a dependency for board item #1520 (paldocs #46 — Ship paldocs.app to TestFlight, sprint:4). Lineage correctly documents this relationship.
    • No conflicts with other sprint:3 items (auth track is independent).

    Acceptance Criteria

    • AC1 (godaddy_dns_record resource) — agent-verifiable via code review
    • AC2 (tofu apply + DNS propagation) — manual, requires TF credentials and apply
    • AC3 (Caddy block on VPS) — manual, not in repo. Constraints correctly note this.
    • AC4 (curl https://paldocs.app returns 200) — manual end-to-end verification
    • AC5 (TLS auto-provisioned) — manual, Caddy handles automatically

    Only AC1 is agent-automatable. ACs 2-5 require manual execution. This is appropriate for an infra ticket — the agent writes the Terraform, the operator applies it and configures Caddy. Test Expectations correctly label items as "Manual".

    Note: GoDaddy secrets prerequisite (~/secrets/godaddy/paldocs.app.env) is mentioned in Context but not in Acceptance Criteria or Constraints. Minor gap — operator should be aware.

    Blast Radius

    Low. Adding a 4th DNS A record to an established pattern. No changes to existing domains. No downstream consumers affected. Caddy reverse proxy addition is similarly isolated — new server block, no modification of existing blocks.

    Decomposition Assessment

    • 1 file target across 1 repo — no decomposition needed
    • 5 acceptance criteria but only 1 is a code change — within limits
    • Estimated agent work: less than 2 minutes (copy existing pattern, change domain name)
    • No decomposition needed

    Recommendation

    • [SCOPE] Create user story entry story-paldocs-ios-testflight on project-paldocs user-stories section — "Ship paldocs to iOS App Store via turbo-ios + TestFlight"
    • [SCOPE] Create architecture note arch-infra for the infrastructure component (Hetzner edge VPS, Caddy, Tailscale mesh, DNS), or decide that arch:infra is a foundational label that does not require a dedicated note.
  • Verdict: READY

    Re-review of board item #1599 after scope refinement. Previous review: review-1599-2026-06-24 (NEEDS_REFINEMENT, 4 BODY fixes). All 4 BODY fixes have been addressed.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Spike #75 (PR #76)
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP cutover operator story
    • [x] Context -- explains gap clearly, now includes correct embedding path and reference code size
    • [x] File Targets -- 4 targets listed (including from_template)
    • [x] Feature Flag -- none (correct, internal infrastructure)
    • [x] Acceptance Criteria -- 7 criteria
    • [x] Test Expectations -- 4 expectations with correct RSpec run command
    • [x] Constraints -- 4 constraints listed (including explicit embedding path constraint)
    • [x] Checklist -- standard
    • [x] Related -- 3 references

    Traceability

    • [x] story:consolidation label -- absorb pal-e-docs into Rails monolith
    • [x] story note verified -- story-paldocs-consolidation found in project-paldocs user-stories section
    • [x] arch:rails-api label -- Rails API component
    • [ ] arch note MISSING -- arch-rails-api note still does not exist. Search returns zero results. This is a cross-cutting gap affecting multiple board items (#1568, #1569, #1570, #1599, #1600, #1601, #1602). Not a blocker for this ticket individually -- the label is correct and the work is well-scoped without the note. [SCOPE] Create architecture note arch-rails-api for the Rails API component.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/78, state: open

    File Targets

    • [x] app/services/block_parser.rb (new) -- app/services/ directory exists (contains ollama_client.rb). Standard Rails convention. OK.
    • [x] app/services/block_compiler.rb (new) -- same directory. OK.
    • [x] app/controllers/api/notes_controller.rb -- verified at HEAD. Has create (line 85), update (line 167), and from_template (line 126). None currently calls any block parsing. Confirmed gap matches issue description.
    • [x] Reference: pal-e-docs blocks/ Python files -- verified in prior review (parser.py=316, compiler.py=148, sync.py=112). Repo not accessible via current API token but files were confirmed in review-1599-2026-06-24.

    Previous BODY Fixes -- Resolution Check

    • [x] Fix 1: Add from_template -- RESOLVED. from_template now listed in File Targets, AC 5 added ("Api::NotesController#from_template stores Block rows for the created note"), integration test added ("note from_template via API -> blocks exist").
    • [x] Fix 2: Embedding path (NOTIFY vs after_save) -- RESOLVED. AC 6 now correctly reads "Block#after_save :enqueue_embedding callback fires for new/updated blocks (existing Rails mechanism via Solid Queue)". Context section also states this explicitly. Constraint 3 added: "Embedding path is Block#after_save -> Solid Queue EmbedBlockJob, NOT Postgres NOTIFY".
    • [x] Fix 3: parser.py line count -- RESOLVED. Context now correctly states "~576 lines across 3 Python files (parser.py=316, compiler.py=148, sync.py=112)".
    • [x] Fix 4: Test command -- RESOLVED. Run command is now "bundle exec rspec spec/services/ spec/requests/api/notes_spec.rb".

    Repo Placement

    OK -- issue filed on ldraney/paldocs, all file targets are in the paldocs repo. Reference files in pal-e-docs are read-only source for the port.

    Dependencies

    • Upstream (done): Issue #57 (API namespace + Notes controller) -- merged. Provides the controller to modify. Controller verified at HEAD with create, update, from_template actions.
    • Upstream (done): Issue #52 (Embedding pipeline) -- merged. Block model has after_save :enqueue_embedding callback with needs_embedding? guard (fires on content/block_type change or new record). EmbedBlockJob enqueues via Solid Queue.
    • Upstream (done): Issue #49 (Schema ownership) -- blocks table owned by Rails.
    • Downstream: Issue #61 (Hybrid search) depends on blocks being created correctly.
    • No blockers: In-progress items (#56 Sprint planning UI, #22 Deploy paldocs, #75 MCP compatibility spike) are all independent of this ticket.

    Acceptance Criteria

    • [x] AC 1: BlockParser.call(html_content) returns array of block hashes -- testable, clear.
    • [x] AC 2: BlockCompiler.call(blocks) reconstructs HTML from block array -- testable, clear.
    • [x] AC 3: Api::NotesController#create stores Block rows -- testable, clear.
    • [x] AC 4: Api::NotesController#update re-parses when html_content changes -- testable, clear. Controller already tracks content_changed boolean.
    • [x] AC 5: Api::NotesController#from_template stores Block rows -- testable, clear. NEW in this revision.
    • [x] AC 6: Block#after_save :enqueue_embedding callback fires -- testable, correct mechanism. Verified: Block model has this callback with needs_embedding? guard.
    • [x] AC 7: Embedding worker picks up blocks -- testable, will work automatically via existing callback.

    All 7 AC are testable by an agent. The test expectations cover all paths (unit round-trip, block types, integration for create and from_template).

    Blast Radius

    • compiled_pages table: Exists in the Rails DB schema (inherited from pal-e-docs migration) but has NO corresponding Rails model. The Python sync.py writes to compiled_pages, but the Rails app does not use it -- the toc and section endpoints work directly from Block rows. NOT a concern for this ticket. The compiled_pages table is a pal-e-docs artifact that can be deprecated in a future cleanup ticket.
    • Nokogiri availability: Constraint says "already in Gemfile" -- Nokogiri is NOT in the explicit Gemfile but IS available as a transitive dependency (v1.19.4 via actionview/loofah/rails-html-sanitizer in Gemfile.lock, 21 references). The agent can require 'nokogiri' without adding it to the Gemfile. Minor wording inaccuracy but does not affect implementation.
    • destroy action: The existing destroy action uses Block.where(note_id: note.id).delete_all which bypasses ActiveRecord callbacks. This is pre-existing and not affected by this ticket.
    • BeautifulSoup to Nokogiri: The port from bs4 to Nokogiri is a well-understood translation. Both libraries have equivalent HTML parsing APIs.

    Decomposition Assessment

    No decomposition needed.

    • File targets: 3 modified/created + references. All in 1 repo. Under threshold.
    • AC count: 7. Over the 5 threshold, but all are tightly coupled to a single concern (block parsing on note write). Splitting would create artificial coupling overhead.
    • Estimated agent time: ~8-10 minutes. Exceeds 5-minute rule, but the 8-point estimate accounts for this. The work is cohesive -- a single service extraction pattern.
    • Phasing guidance from prior review still applies: (1) port parser + compiler as services with unit tests, (2) wire into controller + integration tests.

    Recommendation

    • [SCOPE] Create architecture note arch-rails-api for the Rails API component. Cross-cutting gap affecting 7+ board items. Not a blocker for this specific ticket.

    No BODY or LABEL fixes needed. All previous NEEDS_REFINEMENT items have been resolved. Ticket is ready for implementation.

  • Verdict: APPROVED

    Re-review of board item #1600 after scope refinement. Previous review review-1600-2026-06-24 returned NEEDS_REFINEMENT with two findings. Both have been addressed.

    Previous Findings Resolution

    • [x] [BODY] Test command — FIXED. Changed from bin/rails test test/requests/api/notes_* to bundle exec rspec spec/requests/api/notes_spec.rb. Verified: spec/requests/api/notes_spec.rb exists (799 lines, RSpec format).
    • [x] [SCOPE] arch-rails-api note — ACKNOWLEDGED as cross-cutting gap. The refinement comment correctly identifies this affects all arch:rails-api labeled items, not just this ticket. Not a blocker for this specific scope.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Spike #75 (PR #76) — Gap 9
    • [x] Repo — ldraney/paldocs
    • [x] User Story — well-formed As-a/I-want/So-that
    • [x] Context — clear explanation of pal-e-docs vs Rails gap, now includes explicit status-to-column mapping
    • [x] File Targets — 3 targets listed
    • [x] Feature Flag — none (correct, internal model change)
    • [x] Acceptance Criteria — 3 criteria
    • [x] Test Expectations — corrected to RSpec (bundle exec rspec spec/requests/api/notes_spec.rb)
    • [x] Constraints — 2 constraints listed
    • [x] Checklist — standard 3-item
    • [x] Related — references project and spike

    Traceability

    • [x] story:consolidation label — story-paldocs-consolidation
    • [x] story note verified — found in project-paldocs user-stories section: "absorb pal-e-docs into Rails monolith (schema, API, embeddings)"
    • [x] arch:rails-api label — rails-api component
    • [ ] arch note MISSING — arch-rails-api does not exist in pal-e-docs. Cross-cutting gap affecting all arch:rails-api items. Not a blocker for this ticket.
    • [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/79, open

    File Targets

    • [x] app/controllers/api/notes_controller.rb — verified: exists at HEAD. Update action (lines ~167-233) handles status changes via validate_status! but has no board item sync logic. Correct location for the change.
    • [x] app/models/board_item.rb — verified: exists at HEAD. Has COLUMNS constant and sprint association but no status-to-column mapping method. Correct location for the class method.
    • [x] Reference: pal-e-docs/src/pal_e_docs/routes/notes.py (_status_to_column) — verified by previous review. Mapping now explicitly stated in issue Context section: not-started->backlog, in-progress->in_progress, completed->done, deferred->done, nil->backlog.

    Repo Placement

    OK — issue filed on ldraney/paldocs, all file targets in ldraney/paldocs. Reference to pal-e-docs is read-only (pattern to match). Single-repo change.

    Dependencies

    • Depends on #57 (API namespace + Notes controller) — DONE, merged.
    • Related to Spike #75 (MCP compatibility analysis) — in_progress on board. This ticket was born from Gap 9. Not a hard blocker.
    • No other board items blocked by this ticket.

    Acceptance Criteria

    • [x] AC1: "When a phase note's status changes via PATCH /api/notes/:slug, the linked board_item column updates" — testable via request spec.
    • [x] AC2: "Status-to-column mapping matches pal-e-docs behavior" — testable via unit test with explicit mapping now documented in issue body.
    • [x] AC3: "Non-phase notes are unaffected" — testable via negative request spec.

    All criteria are agent-verifiable. Test command is correct.

    Blast Radius

    Low. The change adds sync logic to the update action only. BoardItem gains a pure class method. No existing behavior modified — sync is additive. The board_items table already has a note_slug column used by pal-e-docs for this purpose.

    Decomposition Assessment

    No decomposition needed. 2 file targets in 1 repo, 3 acceptance criteria, estimated agent work well under 5 minutes. Single focused change.

    Recommendations

    No action needed. Ticket is ready for implementation.

    Standing recommendation (not blocking): Create architecture note arch-rails-api as a separate task to close the cross-cutting traceability gap.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Spike #75 (PR #76)
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP cutover operator story
    • [x] Context -- explains gap clearly
    • [x] File Targets -- 4 targets listed
    • [x] Feature Flag -- none (correct, internal infrastructure)
    • [x] Acceptance Criteria -- 6 criteria
    • [x] Test Expectations -- 4 expectations with run command
    • [x] Constraints -- 3 constraints listed
    • [x] Checklist -- standard
    • [x] Related -- 3 references

    Traceability

    • [x] story:consolidation label -- absorb pal-e-docs into Rails monolith
    • [x] story note verified -- story-paldocs-consolidation found in project-paldocs user-stories section
    • [x] arch:rails-api label -- Rails API component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails-api for the Rails API component. Search returned zero results.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/78, state: open

    File Targets

    • [x] app/services/block_parser.rb (new) -- target directory app/services/ does not exist yet but is standard Rails convention. Will need to be created. OK.
    • [x] app/services/block_compiler.rb (new) -- same as above. OK.
    • [x] app/controllers/api/notes_controller.rb -- verified at HEAD (commit 311f4a4). Has create (line 85) and update (line 167). Neither currently calls any block parsing. Confirmed gap.
    • [x] Reference: pal-e-docs/src/pal_e_docs/blocks/sync.py (112 lines), parser.py (316 lines), compiler.py (148 lines) -- all exist and verified.
    • [ ] MISSING target: Api::NotesController#from_template (line 125) also creates notes with html_content but is not listed. [BODY] Add from_template to File Targets and AC.

    Repo Placement

    OK -- issue filed on ldraney/paldocs, all file targets are in the paldocs repo. Reference files in pal-e-docs are read-only source for the port.

    Dependencies

    • Upstream (done): Issue #57 (API namespace + Notes controller) -- merged as commit 311f4a4. Provides the controller to modify.
    • Upstream (done): Issue #52 (Embedding pipeline) -- merged as commit 7d63900. Provides EmbedBlockJob and Block#after_save :enqueue_embedding callback. When this ticket creates Block rows via ActiveRecord, embeddings will be enqueued automatically.
    • Upstream (done): Issue #49 (Schema ownership) -- blocks table owned by Rails.
    • Downstream: Issue #61 (Hybrid search) depends on embeddings working, which depends on blocks being created. This ticket unblocks #61.
    • No blockers: No in_progress items block this ticket. Sprint:2 items (#56, #22, #75) are independent.

    Acceptance Criteria

    • [x] AC 1: BlockParser.call(html_content) returns block hashes -- testable, clear.
    • [x] AC 2: BlockCompiler.call(blocks) reconstructs HTML -- testable, clear.
    • [x] AC 3: Api::NotesController#create stores blocks -- testable, clear.
    • [x] AC 4: Api::NotesController#update re-parses on content change -- testable, clear. Controller already tracks content_changed boolean.
    • [ ] AC 5: "Postgres NOTIFY fires on new block rows (existing trigger)" -- MISLEADING. The existing Postgres trigger (blocks_embedding_trigger from pal-e-docs Alembic migration) does fire pg_notify on INSERT, but the Rails embedding path uses ActiveRecord after_save callback on Block model to enqueue EmbedBlockJob via Solid Queue. Both paths coexist. [BODY] Reword AC 5: "Block rows created via ActiveRecord trigger after_save embedding enqueue (existing Block model callback)" or split into two: one for the Rails path (Solid Queue) and one noting NOTIFY coexistence.
    • [x] AC 6: Embedding worker picks up blocks -- will work automatically via existing Block#after_save :enqueue_embedding callback.
    • [ ] MISSING AC: from_template action also creates notes with html_content and needs block parsing. [BODY] Add AC: "Api::NotesController#from_template stores Block rows via parser".

    Blast Radius

    • sync.py recompile pattern: The Python parse_and_store_blocks() calls recompile() which updates a compiled_pages table and resets notes.html_content from compiled output. The issue does not mention compiled_pages. If paldocs does not use compiled_pages, this is fine. If it does, the compiler must also update that table. Need to verify whether compiled_pages table exists in Rails schema.
    • parser.py line count: Issue says "~200 lines" but actual parser.py is 316 lines. The port scope is larger than estimated. compiler.py is 148 lines and sync.py is 112 lines. Total reference code: 576 lines.
    • BeautifulSoup vs Nokogiri: Parser uses bs4 (BeautifulSoup). Constraint says "Use Nokogiri" which is correct -- Nokogiri is the Ruby equivalent. Nokogiri is available as a transitive dependency via Rails (loofah), confirmed in Gemfile.lock. However, it is NOT in the explicit Gemfile. [BODY] Add constraint: "Add nokogiri to Gemfile explicitly if needed for direct require, or confirm transitive dependency suffices."
    • Destroy action: The existing Api::NotesController#destroy already does Block.where(note_id: note.id).delete_all -- this bypasses ActiveRecord callbacks (uses delete_all not destroy_all). Not a concern for this ticket but worth noting.

    Decomposition Assessment

    Borderline -- no decomposition needed, but monitor.

    • File targets: 4 (2 new + 1 modified + references). All in 1 repo. Under threshold.
    • AC count: 6 + 1 missing = 7. Over the 5 threshold, but all are tightly coupled to a single concern (block parsing on note write).
    • Estimated agent time: ~10 minutes. The 576 lines of Python reference code must be understood and ported, plus tests. This exceeds the 5-minute rule.
    • Recommendation: The 8-point estimate is appropriate for the scope. The work is cohesive (single service extraction pattern). Decomposition into sub-tickets would create artificial coupling overhead. However, the agent implementing this should be given clear phasing guidance: (1) port parser + compiler as services with unit tests, (2) wire into controller + integration tests.

    Recommendation

    • [BODY] Fix parser.py line count: "~200 lines" should be "~316 lines" (total reference: 576 lines across parser.py, compiler.py, sync.py).
    • [BODY] Add from_template to File Targets and add AC: "Api::NotesController#from_template stores Block rows via parser".
    • [BODY] Reword AC 5 about NOTIFY -- the Rails embedding path is via ActiveRecord after_save callback + Solid Queue, not Postgres NOTIFY. Both coexist on the shared DB but the agent should target the Rails path.
    • [BODY] Add note about compiled_pages table -- clarify whether the Ruby port needs to update compiled_pages or if that table is pal-e-docs-only.
    • [SCOPE] Create architecture note arch-rails-api for the Rails API component. Multiple board items reference this label.
  • Verdict: READY

    Scope is solid. File targets verified against committed codebase. Traceability complete (story note exists; arch note missing but pre-existing gap across all rails-api tickets). Single-repo, 3 file targets, 3 ACs — fits a single agent pass well under 5 minutes.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Spike #75 (PR #76) Gap 7
    • [x] Repo — ldraney/paldocs
    • [x] User Story — present, well-formed
    • [x] Context — clear description of silent failure mode
    • [x] File Targets — 3 targets listed with descriptions
    • [x] Feature Flag — "none" (correct, internal API change)
    • [x] Acceptance Criteria — 3 criteria, verifiable
    • [x] Test Expectations — 3 unit tests + run command
    • [x] Constraints — ERB/Liquid preference, data dict compat, migration path noted
    • [x] Checklist — standard PR/test/no-unrelated
    • [x] Related — project + spike references

    Traceability

    • [x] story:consolidation label — present on board item
    • [x] story note verified — story-paldocs-consolidation found in project-paldocs user-stories section: "absorb pal-e-docs into Rails monolith (schema, API, embeddings)"
    • [x] arch:rails-api label — present on board item
    • [ ] arch note MISSING — no arch-rails-api note exists in pal-e-docs. However, this is a pre-existing gap shared across all 6+ board items with the arch:rails-api label (items #1568, #1569, #1570, #1599, #1600, #1601, #1602). Not a blocker for this ticket specifically — the arch note should be created as a separate backlog item.
    • [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/81, state: open

    File Targets

    • [x] app/controllers/api/notes_controller.rb — verified: exists in git HEAD (commit 311f4a4). Contains from_template action at line 126. Currently copies template.html_content verbatim; data param is permitted in template_params but never used. Confirmed silent failure.
    • [x] app/services/template_renderer.rb (new) — confirmed does not exist yet. app/services/ directory also does not exist. Both need to be created.
    • [x] Reference: pal-e-docs/src/pal_e_docs/routes/notes.py — verified: exists at /home/ldraney/pal-e-docs/src/pal_e_docs/routes/notes.py. /from-template endpoint at line 551-585 calls render_template() service.
    • [x] Reference: pal-e-docs/src/pal_e_docs/services/template_renderer.py — verified: complete Jinja2 implementation. Finds code block with language: "jinja2", renders with data dict, raises 3 custom exceptions (TemplateNotFoundError, TemplateBlockNotFoundError, TemplateRenderError).

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are in paldocs. Reference files in pal-e-docs are read-only reference, not modified. Single-repo change.

    Dependencies

    • Depends on sprint:1 item #1567 (API namespace + Notes controller, 12 endpoints) — DONE. The from_template action and route already exist.
    • Spike #75 (MCP endpoint compatibility) is in_progress — this ticket was identified as Gap 7 from that spike. No hard blocker; spike validates the gap exists but implementation can proceed independently.
    • No downstream blockers identified on the board.

    Acceptance Criteria

    All 3 ACs are verifiable by an agent:

    • AC1: POST with data param renders variables — testable via request spec
    • AC2: Templates without renderable blocks copy verbatim — testable via request spec (existing test already covers this path)
    • AC3: Invalid/missing data keys produce clear error — testable via request spec

    Test run command is accurate: bin/rails test test/requests/api/notes_* test/services/template_renderer_*. Existing spec at spec/requests/api/notes_spec.rb has 2 from_template tests (create success + 404) that will need to be extended.

    Note: Test command uses bin/rails test (Minitest path convention) but existing tests are in spec/ (RSpec). Agent should use bundle exec rspec spec/requests/api/notes_spec.rb spec/services/ instead. Minor issue — agent will adapt.

    Blast Radius

    • MCP tool consumers: The create_note_from_template MCP tool (used by all Claude agents) currently sends a data JSON string. The pal-e-docs FastAPI server renders it; the paldocs Rails server silently ignores it. After this fix, both servers will render templates. Behavior change is the intended fix.
    • Existing templates: Templates in pal-e-docs use Jinja2 syntax in code blocks. The issue's constraint section correctly notes the need to handle existing Jinja2 syntax or define a migration path. ERB uses different delimiters (<%= %> vs {{ }}), so existing templates would need updating if ERB is chosen. Liquid uses {{ }} like Jinja2, which would be more compatible.
    • Hook reference: claude-custom/hooks/block-docs-writes.sh line 33 references mcp__pal-e-docs__create_note_from_template — no change needed, this is a tool name reference not a template format.
    • Backward compatibility: AC2 covers this — templates without renderable blocks must copy verbatim. This is critical since many templates have no code blocks.

    Decomposition Assessment

    No decomposition needed.

    • 3 file targets in 1 repo — under threshold
    • 3 acceptance criteria — under threshold
    • Estimated agent work: straightforward port of well-documented Python logic to Ruby equivalent, well under 5 minutes
    • Clear reference implementation exists

    Recommendation

    No action needed. Ticket is READY for implementation.

    Advisory notes (non-blocking):

    • [SCOPE] Create architecture note arch-rails-api for the Rails API component — shared gap across 6+ board items. Should be a separate backlog item, not a blocker for this ticket.
    • The test run command in the issue uses Minitest conventions but the project uses RSpec. Agent will adapt at implementation time.
  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Spike #75 (PR #76) Gap 4
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP consumer wants project JSON to include page_note
    • [x] Context -- pal-e-docs ProjectOut includes page_note, Rails project_json omits it
    • [x] File Targets -- app/controllers/api/notes_controller.rb
    • [x] Feature Flag -- none (appropriate for internal API field addition)
    • [x] Acceptance Criteria -- 3 criteria, all testable
    • [x] Test Expectations -- 2 unit tests + run command
    • [x] Constraints -- match PageNoteInfo shape exactly
    • [x] Checklist -- standard PR/tests/no-unrelated
    • [x] Related -- references project-pal-e-docs, Spike #75

    Traceability

    • [x] story:consolidation label -- "absorb pal-e-docs into Rails monolith (schema, API, embeddings)"
    • [x] story note verified -- found as story-paldocs-consolidation in project-paldocs user-stories section
    • [x] arch:rails-api label -- references Rails API component
    • [ ] arch note MISSING -- no arch-rails-api note found in pal-e-docs. However, this is a shared label across 6+ board items on board-paldocs (e.g., #57 API namespace, #58 API Boards, #59 API Projects, #78, #79, #80, #81). Creating the arch note is a cross-cutting concern, not a blocker for this 1-point ticket. Flagged as [SCOPE] recommendation.
    • [x] Forgejo issue -- ldraney/paldocs#80, state: open

    File Targets

    • [x] app/controllers/api/notes_controller.rb -- verified exists on main (commit 311f4a4). Contains project_json method at line 470 that currently omits page_note. The method serializes id, name, slug, platform, repo_url, is_public, created_at, updated_at. Adding page_note is straightforward.
    • [x] Project model -- belongs_to :page_note, class_name: "Note", optional: true confirmed in app/models/project.rb line 7. Association already exists.
    • [x] Existing tests at spec/requests/api/notes_spec.rb -- confirmed present, currently has no page_note assertions. Test run command bin/rails test test/requests/api/notes_* in the issue uses minitest path convention but the project uses RSpec (spec/ directory). Agent should use bundle exec rspec spec/requests/api/notes_spec.rb instead.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, fix is in ldraney/paldocs. Single-repo change.

    Dependencies

    Depends on the API namespace (PR #66, already merged to main). No blocking items on the board. Spike #75 (in_progress, board item #1597) is the parent spike but this ticket was explicitly extracted as independent work -- no blocker.

    Acceptance Criteria

    • [x] "project_json includes page_note: {id, title, slug} when project has a page note" -- testable, agent can create a project with a page_note association and assert JSON shape
    • [x] "project_json includes page_note: null when no page note" -- testable, existing project fixture has no page_note
    • [x] "Existing API specs updated to assert the new field" -- testable, clear scope

    All criteria are machine-verifiable. Minor note: the test run command uses minitest convention but project uses RSpec. Agent will figure this out from the codebase.

    Blast Radius

    Low. project_json is called in exactly one place (note_json helper, line 442). Adding a field is additive and non-breaking. The eager-loading includes :project but not project: :page_note -- agent should add this to avoid N+1 queries (6 include sites in the controller). No downstream consumers beyond MCP clients that already expect the field from pal-e-docs.

    Decomposition Assessment

    1 file target, 1 repo, 3 acceptance criteria, estimated agent work under 5 minutes. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails-api for the Rails API component. This affects 6+ tickets on board-paldocs. Not a blocker for this ticket, but should be created before the next sprint.
    • [BODY] Test run command uses minitest path: bin/rails test test/requests/api/notes_*. Project uses RSpec. Should be: bundle exec rspec spec/requests/api/notes_spec.rb. Minor -- agent will self-correct from codebase, but fixing improves template accuracy.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Spike #75 (PR #76) — Gap 9
    • [x] Repo — ldraney/paldocs
    • [x] User Story — well-formed As-a/I-want/So-that
    • [x] Context — clear explanation of pal-e-docs vs Rails gap
    • [x] File Targets — 3 targets listed
    • [x] Feature Flag — none (correct, internal model change)
    • [x] Acceptance Criteria — 3 criteria
    • [ ] Test Expectations — ISSUE: run command uses Minitest convention (bin/rails test test/requests/api/notes_*) but project uses RSpec. Correct command: bundle exec rspec spec/requests/api/notes_spec.rb
    • [x] Constraints — 2 constraints listed
    • [x] Checklist — standard 3-item
    • [x] Related — references project and spike

    Traceability

    • [x] story:consolidation label — story-paldocs-consolidation
    • [x] story note verified — found in project-paldocs user-stories section: "absorb pal-e-docs into Rails monolith (schema, API, embeddings)"
    • [x] arch:rails-api label — rails-api component
    • [ ] arch note MISSING — [SCOPE] search for "arch-rails-api" returned no results in pal-e-docs. Create architecture note arch-rails-api for the Rails API component.
    • [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/79, open

    File Targets

    • [x] app/controllers/api/notes_controller.rb — verified: exists at HEAD (commit 311f4a4). Update action at lines 167-233 handles status changes (lines 197-199) but has no board item sync logic. This is the correct location for the change.
    • [x] app/models/board_item.rb — verified: exists at HEAD. Currently has COLUMNS constant and basic associations but no status-to-column mapping method. Correct location for the class method.
    • [x] Reference: pal-e-docs/src/pal_e_docs/routes/notes.py (_status_to_column) — verified: function at line 310, STATUS_TO_COLUMN dict at line 302. Mapping: not-started→backlog, in-progress→in_progress, completed→done, deferred→done, nil→backlog.

    Repo Placement

    OK — issue filed on ldraney/paldocs, all file targets are in ldraney/paldocs. Reference to pal-e-docs is read-only (pattern to match). Single-repo change.

    Dependencies

    • Depends on #57 (API namespace + Notes controller) — DONE, merged as commit 311f4a4.
    • Related to Spike #75 (MCP compatibility analysis) — currently in_progress on the board. This ticket was born from that spike's Gap 9 finding. Not a hard blocker — the gap is well-defined and the fix is standalone.
    • No other board items blocked by this ticket.

    Acceptance Criteria

    • [x] AC1: "When a phase note's status changes via PATCH /api/notes/:slug, the linked board_item column updates" — testable via request spec. The board_item links to notes via note_slug column in the board_items table.
    • [x] AC2: "Status-to-column mapping matches pal-e-docs behavior" — testable via unit test against the STATUS_TO_COLUMN dict (4 mappings + nil default).
    • [x] AC3: "Non-phase notes are unaffected" — testable via negative request spec (update a doc note's status, verify no board_item changes).

    All criteria are agent-verifiable. Test commands need correction (see recommendation).

    Blast Radius

    Low blast radius. The change adds logic to the update action only. The BoardItem model gains a pure class method. No existing behavior is modified — the sync is additive. The board_items table already has a note_slug column used by pal-e-docs for exactly this purpose.

    No similar gap exists in other controllers — only the API notes controller handles status updates via JSON API.

    Decomposition Assessment

    No decomposition needed. 2 file targets in 1 repo, 3 acceptance criteria, estimated agent work well under 5 minutes. Single focused change.

    Recommendations

    • [BODY] Fix test run command: bin/rails test test/requests/api/notes_*bundle exec rspec spec/requests/api/notes_spec.rb
    • [SCOPE] Create architecture note arch-rails-api for the Rails API component in pal-e-docs. This note should document the API namespace, controller hierarchy, authentication approach, and endpoint inventory.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

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

    All required sections for the Feature template are present.

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:rails-api label -- rails-api component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails-api in pal-e-docs. Search returned zero results.
    • [x] Forgejo issue -- paldocs#59, open

    File Targets

    • [x] app/controllers/api/projects_controller.rb -- to be created (directory exists via #57 merge at 311f4a4)
    • [x] app/controllers/api/repos_controller.rb -- to be created
    • [x] app/controllers/api/tags_controller.rb -- to be created
    • [x] app/controllers/api/search_controller.rb -- to be created
    • [x] config/routes.rb -- exists, API namespace already present from #57
    • [x] Do-not-touch list (base_controller.rb, notes_controller.rb, models, views) -- correct

    ISSUE: The constraint says "list_projects defaults to active projects only (uses scope from #49)" but the projects table has NO status column. Schema columns: id, name, slug, platform, repo_url, created_at, is_public, page_note_id, updated_at. The model defines scope :active, -> { where(status: "active") } which will raise ActiveRecord::StatementInvalid at runtime. The agent implementing this ticket will be blocked on AC #1.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are in paldocs. Single-repo scope.

    Dependencies

    • paldocs#49 (schema ownership) -- closed/done. Upstream satisfied.
    • paldocs#57 (API namespace + notes controller) -- closed/done. Commit 311f4a4 merged. Base controller and API namespace exist in routes.
    • paldocs#58 (boards API) -- backlog, sibling. No blocking dependency. Can proceed in parallel.
    • paldocs#61 (hybrid search) -- backlog, downstream. Will fill the semantic stub. No block.
    • paldocs#52 (embedding pipeline) -- done. Needed for #61, not #59.

    Dependencies correctly documented. No undocumented dependencies found.

    Acceptance Criteria

    8 AC, all verifiable via request specs. Test expectations match (request specs for all 8, integration test for project CRUD, 501 stub check). Run command bundle exec rspec is correct. Existing spec at spec/requests/api/notes_spec.rb provides the pattern.

    AC #1 ("GET /api/projects returns active projects by default") will fail due to missing status column on projects table.

    Blast Radius

    Low risk. Additive API controllers in dedicated namespace. No existing UI controllers modified. The JSON response format constraint ("match pal-e-docs exactly") is achievable -- Notes controller already uses render json with helper methods. The scope :active bug on Project model exists in HEAD but no code currently calls it.

    Decomposition Assessment

    4 new controller files + routes change in 1 repo. 8 AC. Estimated agent work: 3-4 minutes. Within the 5-minute rule. No decomposition needed.

    Recommendations

    • [BODY] Fix constraint and AC #1: the projects table has no status column. Either (a) add a file target for a migration adding status varchar(50) DEFAULT 'active' to projects, or (b) change the constraint to filter by is_public instead and update AC #1 to say "returns public projects by default." Also fix the existing scope :active on Project model to match whichever approach is chosen.
    • [SCOPE] Create architecture note arch-rails-api in pal-e-docs. This label is shared by 4 board items (#57, #58, #59, #61) and currently has no backing note.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- decomposed from paldocs#51, depends on #49 and #57
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present, well-formed
    • [x] Context -- clear background on decomposition and MCP migration
    • [x] File Targets -- 3 files to create/modify, 3 files not-to-touch listed
    • [x] Feature Flag -- none (additive API routes, correct)
    • [x] Acceptance Criteria -- 5 criteria
    • [x] Test Expectations -- request specs, integration test, response shape comparison
    • [x] Constraints -- as_json pattern, JSON shape matching, no jbuilder
    • [x] Checklist -- present
    • [x] Related -- project, story, arch, upstream/sibling/downstream all listed

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:rails-api label -- present on board item
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails-api for the Rails API component
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/58, open

    File Targets

    • [x] app/controllers/api/boards_controller.rb -- to be created. api/ directory confirmed present from #57.
    • [x] app/controllers/api/board_items_controller.rb -- to be created. Same api/ directory.
    • [x] config/routes.rb -- to be modified. Has namespace :api block with notes routes from #57.
    • [x] app/controllers/api/base_controller.rb -- correctly listed as do-not-touch.
    • [x] app/models/ -- correctly listed as do-not-touch. BoardItem model exists. Boards are Notes.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets in same repo. Single-repo change.

    Dependencies

    • [x] Upstream #49 (schema ownership) -- closed/merged
    • [x] Upstream #57 (API namespace + notes controller) -- closed/merged
    • [x] Sibling #59 (projects/repos/tags/search API) -- open, no blocking dependency
    • [x] Downstream #54 (MCP server) -- open, sprint:3, correctly sequenced

    Acceptance Criteria

    5 criteria, all verifiable by an agent via request specs and JSON shape comparison.

    Blast Radius

    Existing UI controllers in separate namespace, no collision. Routes additive under namespace :api. Sync endpoint needs pal-e-docs Python source reference.

    Decomposition Assessment

    3 file targets in 1 repo. 5 AC. 12 endpoints following established pattern. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails-api for the Rails API component. Used by #58, #59, and #54 but no backing note exists.
  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- decomposed from paldocs#52, depends on #52 and #59
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP layer needs Rails semantic and hybrid search endpoints
    • [x] Context -- explains relationship to #52 (embeddings) and #59 (search stub)
    • [x] File Targets -- 2 files to create/modify, 3 files excluded with rationale
    • [x] Feature Flag -- none (replaces stub)
    • [x] Acceptance Criteria -- 3 criteria, all testable
    • [x] Test Expectations -- unit + 2 integration tests, run command specified
    • [x] Constraints -- RRF formula, neighbor gem usage, tsvector dependency documented
    • [x] Checklist -- standard 5-item checklist
    • [x] Related -- project, story, arch note, upstream/downstream issues all listed

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:rails-api label -- rails-api component
    • [x] arch note verified -- arch-rails-api note exists in pal-e-docs
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/61, open

    File Targets

    • [x] app/services/hybrid_search.rb -- to be created. Services directory pattern established by ollama_client.rb on main.
    • [x] app/controllers/api/search_controller.rb -- to be created or modified from #59 stub. Does not exist on main yet.
    • [x] app/models/block.rb -- correctly excluded. Has has_neighbors :embedding from #52.
    • [x] app/jobs/ -- correctly excluded. EmbedBlockJob exists from #52.
    • [x] db/migrate/ -- correctly excluded. No schema changes needed.

    Repo Placement

    OK. Single-repo ticket, all targets in ldraney/paldocs.

    Dependencies

    • paldocs#52 (embedding pipeline) -- CLOSED, merged on main. Dependency satisfied.
    • paldocs#59 (search controller stub) -- OPEN. Non-blocking: agent can create controller directly without stub.
    • paldocs#54 (MCP server) -- downstream, sprint:3, no conflict.
    • Note.keyword_search exists in note.rb using PostgreSQL tsvector -- available for RRF keyword leg.

    Acceptance Criteria

    3 criteria, all agent-verifiable. Tests need blocks with pre-computed embeddings (mock or fixture vectors).

    Blast Radius

    Low. Adds new files and routes only. No modification to existing code. Only shared touchpoint is config/routes.rb.

    Decomposition Assessment

    No decomposition needed. 2 file targets, 1 repo, 3 AC. Under 5 minutes.

    Recommendation

    No action needed. Scope is solid, traceability complete, file targets verified, dependencies satisfied or non-blocking.

  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- references paldocs#50 upstream dependency
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- well-formed As/I want/So that
    • [x] Context -- clear explanation of sprint workflow and relationship to #50
    • [x] File Targets -- 6 files to create, 3 exclusions listed with reasons
    • [x] Feature Flag -- "none" with rationale (new routes, no impact)
    • [x] Acceptance Criteria -- 6 testable criteria
    • [x] Test Expectations -- controller tests + system test + run command
    • [x] Constraints -- dependency, Hotwire requirement, "functional first" noted
    • [x] Checklist -- standard items present
    • [x] Related -- project, story, arch, upstream/downstream all listed

    Traceability

    • [x] story:sprint-orchestration label -- present on board item
    • [x] story note verified -- story-paldocs-sprint-orchestration exists in pal-e-docs (note #2093) AND listed in project-paldocs user-stories section
    • [x] arch:rails-views label -- present on board item
    • [ ] arch note MISSING -- arch-rails-views not found in pal-e-docs. However, this is a soft gap: the ticket is self-contained enough to execute without the arch note. The arch note should document ERB/Hotwire patterns used in paldocs. [SCOPE] Create architecture note arch-rails-views for the Rails views component.
    • [x] Forgejo issue -- ldraney/paldocs#56, state: open

    File Targets

    • [x] app/controllers/sprint_planning_controller.rb -- verified: does NOT exist yet (correct, new file)
    • [x] app/views/sprint_planning/index.html.erb -- verified: directory does not exist yet (correct, new files)
    • [x] app/views/sprint_planning/show.html.erb -- verified: new file
    • [x] app/views/sprint_planning/_wave.html.erb -- verified: new file (partial)
    • [x] config/routes.rb -- verified: exists, currently has resources for projects/notes/boards/board_items. Sprint routes will be added.
    • [x] app/views/layouts/application.html.erb -- verified: exists, navbar has only brand link. Sprint planning nav link to be added.
    • [x] Exclusions verified: db/migrate/, app/models/, db/structure.sql correctly excluded (owned by paldocs#50)

    Dependency Verification

    • [x] paldocs#50 (sprint schema) -- Forgejo issue CLOSED, commit 25dcc57 merged to main. Sprint model, SprintProject model, board_items.sprint_id column all present on main branch.
    • [x] Models verified on main: Sprint (with statuses: planning/active/complete, has_many sprint_projects/board_items), SprintProject (belongs_to sprint/project), BoardItem (belongs_to sprint, optional: true)
    • [x] Board item #1560 for paldocs#50 is in validation column -- merged but awaiting post-merge validation. This does NOT block #56 since the code is on main.

    Repo Placement

    Correct. Issue filed on ldraney/paldocs, all file targets are in the paldocs repo. Single-repo ticket.

    Dependencies

    • paldocs#50 (sprint schema) -- SATISFIED. Merged to main.
    • No other board items block this ticket.
    • Downstream: this ticket enables sprint planning for waves 2-4 of the consolidation work.

    Acceptance Criteria

    6 criteria, all agent-verifiable:

    • Nav link presence -- checkable via system test or controller test
    • Sprint creation with project selection -- CRUD action testable
    • Board items grouped by column -- query and view testable
    • Side-by-side project boards -- view structure testable
    • Wave staging (sprint_id + wave_number) -- model update testable
    • Wave grouping with point totals -- view rendering testable

    Test expectations include controller tests (3) and a system test (1). Run command: bundle exec rspec. All reasonable.

    Blast Radius

    Low. All new files except two append-only modifications:

    • config/routes.rb -- adding sprint_planning resource routes (no modification to existing routes)
    • application.html.erb -- adding one nav link to header (no modification to existing layout structure)

    No sibling services affected. No shared components modified. Existing kanban board, project pages, and notes views are untouched.

    Decomposition Assessment

    6 file targets in 1 repo. 6 acceptance criteria. Estimated agent work: 3-5 minutes (standard Rails controller + views + routes). All within a single pass for a 3pt ticket. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails-views documenting ERB template patterns, Hotwire (Turbo + Stimulus) conventions, and view directory structure used in paldocs. This is a soft gap -- the ticket is executable without it, but the note should exist for traceability.

    No other action needed. Ticket is well-scoped, dependency is satisfied, and all file targets are verified.

  • Verdict: APPROVED

    Re-review of board item #1560. Prior review (review-1560-2026-06-23) returned NEEDS_REFINEMENT with 4 issues. All 4 have been resolved.

    Prior Issues -- Verification

    • [x] arch-rails-db note missing -- FIXED. arch-rails-db exists as architecture note in paldocs project. Contains ER diagram, components table, key decisions about schema_format and pgvector.
    • [x] story-paldocs-sprint-orchestration had wave references -- FIXED. Title now "Sprint Orchestration -- cross-project sprint planning UI". Acceptance criteria say "flat parallel batch with no internal dependencies". No "wave" appears anywhere in the note.
    • [x] Board item title stale -- FIXED. Board item #1560 title is "Sprint schema -- migrations + models for sprint tracking".
    • [x] Schema format unclear -- FIXED. New "Schema Format" section added to issue body. States config.active_record.schema_format = :sql, producing db/structure.sql via pg_dump, and mentions regenerating db/ci_schema.sql for CI. Verified against HEAD: config is :sql, structure.sql exists (841 lines).

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- present, references paldocs#49 dependency
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present, well-formed
    • [x] Context -- present, includes wave_number removal rationale and data model overview
    • [x] File Targets -- present, 7 files (3 migrations, 4 models) with clear modify/create vs do-not-touch sections
    • [x] Schema Format -- present (new section), clarifies :sql format and structure.sql
    • [x] Feature Flag -- none (correct, internal model change with no user-visible impact)
    • [x] Acceptance Criteria -- 5 criteria, all testable via Rails console/RSpec
    • [x] Test Expectations -- 4 test items + run command (bundle exec rspec)
    • [x] Constraints -- present, documents dependency, status enum values, nullability, unique index
    • [x] Checklist -- present
    • [x] Related -- present, references project, story, arch notes, upstream/downstream issues

    All required sections for template-issue-feature are present and complete.

    Traceability

    • [x] story:sprint-orchestration label -- present on board item
    • [x] story note verified -- story-paldocs-sprint-orchestration exists in pal-e-docs (note_type: user-story, project: paldocs). Listed in project-paldocs user-stories section.
    • [x] arch:rails-db label -- present on board item
    • [x] arch note verified -- arch-rails-db exists in pal-e-docs (note_type: architecture, project: paldocs). Contains ER diagram and schema decisions.
    • [x] Forgejo issue -- paldocs#50, open
    • [x] sprint:1 label -- present, consistent with sprint batch

    File Targets

    • [x] db/migrate/XXXXXX_create_sprints.rb -- new file, does not exist yet (correct)
    • [x] db/migrate/XXXXXX_create_sprint_projects.rb -- new file, does not exist yet (correct)
    • [x] db/migrate/XXXXXX_add_sprint_id_to_board_items.rb -- new file, does not exist yet (correct)
    • [x] app/models/sprint.rb -- new file, does not exist yet (correct)
    • [x] app/models/sprint_project.rb -- new file, does not exist yet (correct)
    • [x] app/models/board_item.rb -- verified exists. Currently has COLUMNS constant and board association. Sprint belongs_to addition is valid.
    • [x] app/models/project.rb -- verified exists. Currently has notes, repos, page_note associations. Sprint has_many through addition is valid.

    Exclusions (db/structure.sql auto-generated, controllers, views) are sensible and well-documented.

    Repo Placement

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

    Dependencies

    • paldocs#49 (schema ownership transfer) -- CLOSED/MERGED (commit 3576a91). Board item #1559 in done column. Dependency satisfied.
    • paldocs#56 (sprint planning UI) -- DOWNSTREAM, depends on this ticket. In backlog, sprint:2 label. No blocking issue.
    • paldocs#67 (docs: codify sprint philosophy) -- RELATED, in next_up, sprint:1. Parallel work, no dependency.
    • paldocs#52 (embedding pipeline) -- in next_up, sprint:1. No dependency relationship. Can run in parallel.

    Acceptance Criteria

    5 acceptance criteria, all testable via Rails console or RSpec model specs. Run command (bundle exec rspec) is real and matches the project's test setup. No missing criteria. Criteria correctly scope to model/schema layer only, leaving UI concerns to #56.

    Blast Radius

    Low. New tables and new model files. The only modifications to existing models are adding associations to BoardItem (belongs_to :sprint) and Project (has_many :sprint_projects, :sprints through). Both are additive. The board_items.sprint_id column is nullable, so existing records are unaffected. No similar patterns exist elsewhere in the codebase.

    Decomposition Assessment

    7 file targets, 1 repo, 5 AC. Estimated agent time: 3-4 minutes. Three new migrations + two new models + two model updates form a single coherent unit. No decomposition needed.

    Recommendation

    No action needed. All prior issues resolved. Scope is solid, traceability complete, file targets verified, fits in a single agent pass.

  • Verdict: APPROVED

    Retroactive review of board item #1562. This item reached next_up with PR #65 open and rebased because the check-board-advance hook was fail-open. A prior review (review-1562-2026-06-22-r2) already reached APPROVED on 2026-06-22 after a two-round review-fix-re-review loop. This retroactive review confirms the prior approval still holds against the current state of the issue, board item, and PR.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#49 (merged), search split to paldocs#61
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- "As the platform / I need Rails to generate vector embeddings / So that the Python embedding worker can be deprecated"
    • [x] Context -- thorough: describes current Python pipeline, Rails replacement architecture, and coexistence strategy with race-avoidance plan
    • [x] File Targets -- 5 files (3 new, 2 modified) with "do not touch" entries and rationale. Notes app/services/ may need to be created.
    • [x] Feature Flag -- "none" with justification (replaces existing functionality)
    • [x] Acceptance Criteria -- 4 ACs in testable When/Then format
    • [x] Test Expectations -- 3 unit tests plus run command (bundle exec rspec)
    • [x] Constraints -- Ollama endpoint, model (qwen3-embedding:4b), dimensions (2560), block granularity, mermaid skip, queue depth (~5600), Solid Queue
    • [x] Checklist -- 7 items
    • [x] Related -- project, story, arch note, upstream/downstream issues

    All required sections for the Feature template are present and complete.

    Traceability

    • [x] story:consolidation label -- present on board item
    • [x] story note verified -- story-paldocs-consolidation exists (note ID 2092, user-story type, project paldocs). Listed in project-paldocs user-stories section.
    • [x] arch:rails-jobs label -- present on board item
    • [x] arch note verified -- arch-rails-jobs exists (note ID 2095, architecture type, project paldocs). Contains sequence diagram matching the described flow.
    • [x] Forgejo issue -- ldraney/paldocs#52, state: open

    Traceability triangle is complete.

    File Targets

    • [x] app/models/block.rb -- verified on main (5 lines, belongs_to :note). PR branch adds has_neighbors + after_save callback as specified.
    • [x] app/jobs/embed_block_job.rb -- PR creates this. app/jobs/ exists on main with application_job.rb. Implementation includes mermaid skip, text extraction, retry logic.
    • [x] app/services/ollama_client.rb -- PR creates this. app/services/ did not exist; PR creates the directory. HTTP client with typed errors (Error, TimeoutError).
    • [x] config/initializers/ollama.rb -- PR creates this. ENV-based config for URL, model, dimensions, timeout, input prefix.
    • [x] Gemfile -- PR adds neighbor gem (line 18) and webmock for test HTTP stubbing (line 48). solid_queue already present (line 26).

    All 5 file targets verified against both the issue spec and the actual PR diff (11 files changed, 483 insertions). PR also adds spec files and ci_schema.sql updates -- all appropriate for the scope.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets within paldocs. Single-repo scope. No mismatch.

    Dependencies

    • paldocs#49 (schema ownership transfer) -- board item #1559, now in done column (state: closed). Blocker resolved. structure.sql on main has the embedding column (vector(2560)), pgvector extension, and HNSW index.
    • Downstream: paldocs#61 (hybrid search) -- board item #1570, backlog. Depends on embeddings from this ticket. Correctly separated.
    • No blocking dependencies remain. Parallel sprint:1 items (#50 sprint schema, #67 docs) do not conflict.

    Acceptance Criteria

    4 ACs, all automatable:

    • AC1 (job enqueued on block save) -- verified in PR: spec/models/block_spec.rb has 4 tests including enqueue on create, enqueue on content update, skip on position-only change
    • AC2 (Ollama call + vector write) -- verified in PR: spec/jobs/embed_block_job_spec.rb has 8 tests covering the full job flow
    • AC3 (mermaid skip with embedding_status='skipped') -- verified in PR: job checks block_type == "mermaid" and also skips blank content
    • AC4 (retry with exponential backoff, 3 attempts) -- verified in PR: retry_on with polynomially_longer, attempts: 3, for both TimeoutError and Error

    All ACs are covered by tests. PR reports 47 examples, 0 failures locally.

    Blast Radius

    • No existing embedding code on main branch -- this is net-new Rails functionality replacing external Python worker.
    • Coexistence strategy is documented: both systems write to blocks.embedding, last-writer-wins during transition.
    • update_columns used in job to bypass callbacks -- avoids re-enqueue loop. Sound pattern.
    • No search endpoints included -- blast radius contained by the split to #61.

    Decomposition Assessment

    No decomposition needed.

    • File targets: 5 files (3 new, 2 modified) in 1 repo -- under threshold
    • Acceptance criteria: 4 ACs -- under 5 AC threshold
    • Estimated agent work: Under 5 minutes. PR is already implemented with 483 insertions across standard Rails patterns.

    Recommendation

    No action needed. Prior APPROVED verdict from review-1562-2026-06-22-r2 confirmed. Issue scope is clean, implementation matches spec, all traceability links verified, upstream dependency resolved.

  • Verdict: APPROVED

    Note: Retroactive review -- PR #66 already merged (commit 311f4a4). The check-board-advance hook was fail-open so this review was skipped at gate time. Reviewing anyway for audit completeness.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- decomposed from paldocs#51, depends on #49
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP layer needs Rails API endpoints
    • [x] Context -- thorough, explains consolidation motivation and tsvector need
    • [x] File Targets -- 6 files listed (create + modify), plus "do not touch" boundary
    • [x] Feature Flag -- "none" with valid justification (new namespace, no UI impact)
    • [x] Acceptance Criteria -- 6 criteria covering CRUD, search, links, revisions, shape parity
    • [x] Test Expectations -- 4 items with run command
    • [x] Constraints -- 4 constraints (no jbuilder, JSON shape match, tsvector, no migration)
    • [x] Checklist -- 9 items
    • [x] Related -- project, story, arch, upstream/sibling/downstream references

    Traceability

    • [x] story:consolidation label -- present on board item
    • [ ] story note MISSING -- story-paldocs-consolidation note does not exist in pal-e-docs. However, the project page project-paldocs user-stories section lists story-paldocs-consolidation as a link. The backing note has not been created yet. [SCOPE] Create user story note story-paldocs-consolidation.
    • [x] arch:rails-api label -- present on board item
    • [ ] arch note MISSING -- search for arch-rails-api returned no results. The issue references it in the Related section but the note does not exist. [SCOPE] Create architecture note arch-rails-api for the Rails API namespace component.
    • [x] Forgejo issue -- ldraney/paldocs#57, state: closed (expected, PR merged)
    • [x] sprint:1 label -- present, consistent with sprint batch

    File Targets

    • [x] app/controllers/api/base_controller.rb -- verified: created in PR #66, skip CSRF, JSON error handling
    • [x] app/controllers/api/notes_controller.rb -- verified: created, 12 public actions (index, search, show, create, from_template, update, destroy, links, update_links, revisions, toc, section)
    • [x] app/models/note_link.rb -- verified: created, belongs_to source/target
    • [x] app/models/note_revision.rb -- verified: created, belongs_to note
    • [x] app/models/note.rb -- verified: updated with tsvector search scope (keyword_search), has_many :note_links_as_source/target, has_many :note_revisions
    • [x] config/routes.rb -- verified: namespace :api with notes routes including all 12 endpoints
    • [x] db/ci_schema.sql -- bonus: updated for CI test support (not in original file targets but appropriate, adds note_links and note_revisions tables to CI schema)
    • [x] spec/requests/api/notes_spec.rb -- verified: 47 test cases covering all 12 endpoints plus CRUD lifecycle and unit search tests
    • [x] No migrations added -- constraint honored, tables exist from pg_dump
    • [x] No UI views/controllers touched -- constraint honored

    Repo Placement

    OK. Issue filed on ldraney/paldocs, code merged to ldraney/paldocs. Single-repo change as expected.

    Dependencies

    • [x] Upstream: paldocs#49 (schema ownership) -- in done column, confirmed merged (commit 3576a91)
    • [x] Siblings: paldocs#58 (boards API, sprint:2 backlog), paldocs#59 (projects/repos/tags API, sprint:2 backlog) -- correctly sequenced after this ticket
    • [x] Downstream: paldocs#54 (MCP server, sprint:3 backlog) -- correctly waiting for all API endpoints
    • No blocking dependencies remain. Dependency chain is clean.

    Acceptance Criteria

    All 6 acceptance criteria are agent-verifiable:

    • [x] GET /api/notes/:slug returns correct shape -- testable via request spec
    • [x] POST /api/notes creates note -- testable via request spec
    • [x] GET /api/notes/search via tsvector -- testable via request spec
    • [x] GET /api/notes/:slug/links returns links -- testable via request spec
    • [x] GET /api/notes/:slug/revisions returns history -- testable via request spec
    • [x] JSON shape matches pal-e-docs -- partially testable (spec verifies key structure; full shape parity requires runtime comparison against live pal-e-docs, which is a downstream concern for the MCP migration ticket #54)

    Blast Radius

    Low blast radius. The /api/ namespace is entirely new and isolated from existing UI routes. The Note model changes (new associations, search scope) are additive and do not alter existing behavior. The ci_schema.sql change is test-only infrastructure. No downstream consumers are broken.

    Decomposition Assessment

    6 file targets in 1 repo, 6 acceptance criteria. Slightly above the 5-minute rule thresholds (6 AC vs 5 limit), but the work is cohesive: all 12 endpoints share one controller and one model layer. Decomposing Notes CRUD from Notes search/links/toc would create artificial coupling. The 5-point estimate was appropriate. No decomposition needed retroactively.

    Recommendation

    The ticket scope was solid and the implementation matches the spec. Two traceability gaps should be closed:

    • [SCOPE] Create user story note story-paldocs-consolidation in pal-e-docs -- the project page references it but the backing note does not exist.
    • [SCOPE] Create architecture note arch-rails-api in pal-e-docs -- the issue references it and the board label uses it, but no backing note exists.

    These are documentation gaps, not code gaps. The implementation is complete and correct.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- present, references paldocs#49
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present
    • [x] Context -- present, includes wave_number removal rationale
    • [x] File Targets -- present, 7 files listed (3 migrations, 4 models)
    • [x] Feature Flag -- none (correct, internal model change)
    • [x] Acceptance Criteria -- 5 criteria
    • [x] Test Expectations -- 4 test items + run command
    • [x] Constraints -- present
    • [x] Checklist -- present
    • [x] Related -- present

    All required sections for template-issue-feature are present.

    Traceability

    • [x] story:sprint-orchestration label -- present on board item
    • [x] story note verified -- story-paldocs-sprint-orchestration exists in pal-e-docs (note_type: user-story, project: paldocs). Listed in project-paldocs user-stories section.
    • [ ] story note STALE -- story note title says "cross-project wave planning UI" and acceptance criteria reference "named waves" and "Wave view" -- contradicts the revised sprint philosophy (sprints are flat parallel batches, no waves). [SCOPE] Update story-paldocs-sprint-orchestration to remove all wave references.
    • [x] arch:rails-db label -- present on board item
    • [ ] arch note MISSING -- arch-rails-db does not exist in pal-e-docs. Search returned zero results. [SCOPE] Create architecture note arch-rails-db for the Rails database schema component.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/50 -- open

    File Targets

    • [x] db/migrate/XXXXXX_create_sprints.rb -- new file, does not exist yet (correct)
    • [x] db/migrate/XXXXXX_create_sprint_projects.rb -- new file, does not exist yet (correct)
    • [x] db/migrate/XXXXXX_add_sprint_id_to_board_items.rb -- new file, does not exist yet (correct)
    • [x] app/models/sprint.rb -- new file, does not exist yet (correct)
    • [x] app/models/sprint_project.rb -- new file, does not exist yet (correct)
    • [x] app/models/board_item.rb -- verified exists at /home/ldraney/paldocs/app/models/board_item.rb. Currently has basic COLUMNS constant and board association. Sprint association addition is valid.
    • [x] app/models/project.rb -- verified exists at /home/ldraney/paldocs/app/models/project.rb. Currently has notes, repos, and page_note associations. Sprint association addition is valid.

    Exclusions (db/structure.sql, controllers, views) are sensible.

    Note: No structure.sql exists in the repo. The app uses schema_format = :sql style inherited from the shared DB. The spec should clarify whether rails db:migrate generates structure.sql or schema.rb -- agent needs to know which schema dump format is in use.

    Repo Placement

    OK. Issue is filed on ldraney/paldocs, all file targets are in paldocs. Single-repo change.

    Dependencies

    • paldocs#49 (schema ownership transfer) -- CLOSED/MERGED. Dependency satisfied. Board item #1559 is in done column.
    • paldocs#56 (sprint planning UI) -- DOWNSTREAM, depends on this ticket. Open, in backlog. No blocking issue.
    • paldocs#67 (docs: codify sprint philosophy) -- RELATED, in next_up. Should be done in parallel or before this to formalize the no-waves decision.
    • paldocs#52 (embedding pipeline) -- in next_up, same sprint:1. No dependency relationship. Can run in parallel.

    Acceptance Criteria

    5 acceptance criteria. All are testable via Rails console or RSpec model specs. Run command (bundle exec rspec) is real. No missing criteria detected.

    The criteria correctly test only the model/schema layer without touching UI concerns. Good separation from #56.

    Blast Radius

    Low. New tables and new model files. The only modification to existing models is adding associations to BoardItem and Project. No existing functionality is altered. The board_items table modification (adding nullable sprint_id FK) is additive-only.

    No similar patterns in sibling services -- this is the first sprint schema in the codebase.

    Decomposition Assessment

    7 file targets, 1 repo, 5 AC, ~3-4 minutes estimated agent time. This is within the 5-minute rule. Three new migrations + two new models + two model updates is a single coherent unit. No decomposition needed.

    Recommendation

    • [BODY] Board item title on board-paldocs says "Sprint schema -- migrations + models for wave planning" but the spec removed waves. Update board item title to match the revised Forgejo issue title: "Sprint schema -- migrations + models for sprint tracking".
    • [SCOPE] Create architecture note arch-rails-db for the Rails database schema component. This arch label is used by multiple board items (#1388, #1344, #1559, #1560).
    • [SCOPE] Update story-paldocs-sprint-orchestration to remove wave references. Title says "cross-project wave planning UI", acceptance criteria reference "named waves" and "Wave view". These contradict the revised sprint philosophy (flat parallel batches, no waves).
    • [BODY] Clarify schema dump format in Constraints section -- does the project use schema_format = :sql (structure.sql) or :ruby (schema.rb)? No schema dump file exists currently. The agent needs to know which format to expect after rails db:migrate.
  • Verdict: APPROVED

    Retroactive post-merge review. Board item #1559 reached done via PR #60 (merged 2026-06-23) while check-board-advance was fail-open. This review confirms scope compliance after the fact. Note: the ticket was properly reviewed before implementation -- see review-1559-2026-06-22 (NEEDS_REFINEMENT) and review-1559-2026-06-22-r2 (APPROVED). The gate failure was in column advancement enforcement, not in review execution.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- standalone, supersedes pal-e-api#282
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present and clear
    • [x] Context -- thorough background on ownership transfer rationale, includes application.rb comment update
    • [x] File Targets -- 5 modify/create targets, 2 exclusions
    • [x] Feature Flag -- none, justified (default status preserves behavior)
    • [x] Acceptance Criteria -- 6 items
    • [x] Test Expectations -- correct: bundle exec rspec
    • [x] Constraints -- 4 items, well-scoped
    • [x] Checklist -- present
    • [x] Related -- present, references story and arch notes

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation note exists (id 2092)
    • [x] story note verified -- found in project-paldocs user-stories section (4th entry: "absorb pal-e-docs into Rails monolith")
    • [x] arch:rails-db label -- arch-rails-db note exists (id 1840) with ER diagram, components table, key decisions
    • [x] arch note verified -- arch-rails-db note exists in pal-e-docs, tags: architecture, active
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/49, closed (merged via PR #60)

    File Targets -- Post-Merge Verification

    All 5 file targets from the issue were delivered in PR #60. Additionally, 2 spec files and ci_schema.sql were updated (appropriate extras, not scope creep).

    • [x] db/structure.sql -- created: 841 lines, pg_dump of production paledocs schema including pgvector extension, all tables, indexes, triggers, foreign keys
    • [x] config/application.rb -- verified: schema_format = :sql set; ownership comment updated from "managed by pal-e-docs FastAPI" to "Rails is the sole migration authority for the paledocs database"; dump_schema_after_migration = false and migration_error = false set
    • [x] Gemfile -- verified: gem "neighbor" added (line 18) with comment explaining pgvector support
    • [x] db/migrate/20260622120000_add_status_to_projects.rb -- created: adds status VARCHAR(50) DEFAULT 'active' to projects table. Clean, additive migration.
    • [x] app/models/project.rb -- verified: 3 scopes added (.active, .archived, .by_status(s))

    Extra files (not in scope but appropriate):

    • [x] db/ci_schema.sql -- updated to include status column for CI environment
    • [x] spec/models/project_spec.rb -- 4 tests covering all 3 scopes + default status value
    • [x] spec/migrations/add_status_to_projects_spec.rb -- 2 tests covering column type, default, and limit

    Repo Placement

    OK. Issue filed on ldraney/paldocs, PR #60 merged to paldocs main. Single-repo change as scoped.

    Dependencies

    • This was wave:0 -- the foundation for the consolidation sprint. Downstream tickets (#50 sprint schema, #52 embedding pipeline, #57 API endpoints) are correctly sequenced after this.
    • No blocking dependencies existed. Production database access was required for pg_dump -- constraint was met.

    Acceptance Criteria -- Post-Merge Assessment

    • [x] AC 1: rails db:schema:dump generates db/structure.sql -- delivered (841-line structure.sql present)
    • [x] AC 2: rails db:migrate adds status column -- delivered (migration 20260622120000 adds status to projects)
    • [x] AC 3: Project.active returns only active projects -- delivered (scope in model, tested in spec)
    • [x] AC 4: Project.all returns all projects -- inherent ActiveRecord behavior, no filter applied
    • [x] AC 5: neighbor gem loads and vector columns accessible -- delivered (gem in Gemfile, no model-level vector changes yet per scope)
    • [~] AC 6: pal-e-docs Alembic no longer generates migrations -- coordination concern, not verifiable from paldocs repo. Acceptable as manual check.

    Blast Radius

    • pal-e-docs (FastAPI) -- unaffected. Adding status VARCHAR(50) DEFAULT 'active' is additive. Existing rows get default value automatically.
    • pal-e-mcp -- unaffected. Proxies to pal-e-docs API, no schema dependency.
    • Embedding worker -- unaffected. Writes to blocks.embedding, not projects table.
    • CI -- ci_schema.sql updated to include status column, keeping CI test database aligned.

    Decomposition Assessment

    5 file targets, 1 repo, 6 acceptance criteria, single PR. Correctly sized for a single agent pass. No decomposition was needed.

    Scope Issues That Should Have Been Caught by Gate

    None. The ticket was properly scoped and reviewed before implementation. The review-fix-re-review loop worked correctly:

    1. review-1559-2026-06-22 -- NEEDS_REFINEMENT (4 issues: wrong test command, missing comment update in targets, missing story note, missing arch note)
    2. All 4 issues were fixed in the issue body and pal-e-docs
    3. review-1559-2026-06-22-r2 -- APPROVED

    The failure was in check-board-advance being fail-open, which allowed the board item to advance to done without the hook verifying the APPROVED review note existed. The scope review process itself functioned correctly.

    Recommendation

    No action needed. Scope was solid, implementation matches spec, review process was followed. The only process gap was the fail-open check-board-advance hook -- that is tracked separately in claude-custom.

  • Verdict: APPROVED

    Scope is solid. All file targets verified against codebase at HEAD. Template complete. Story note confirmed. Docs-only change with minimal blast radius. Single-agent pass under 5 minutes.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Post-consolidation cleanup, references PR #60
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present, well-formed
    • [x] Context -- clear motivation (stale docs + sprint philosophy)
    • [x] File Targets -- 5 files to touch, 1 excluded, all verified
    • [x] Feature Flag -- "none" (docs-only, appropriate)
    • [x] Acceptance Criteria -- 6 items, all grep-verifiable
    • [x] Test Expectations -- 2 items (link resolution, cross-references)
    • [x] Constraints -- clear (no app code, concise philosophy doc)
    • [x] Checklist -- present
    • [x] Related -- references project page, arch note, story, upstream PR

    Traceability

    • [x] story:sprint-orchestration label -- present on board item
    • [x] story note verified -- story-paldocs-sprint-orchestration found in project-paldocs user-stories section
    • [x] arch:docs label -- present on board item
    • [ ] arch note MISSING -- No arch-docs note found in pal-e-docs. However, arch:docs is a generic "documentation work" component per convention-architecture-ids, not a specific architecture component. Acceptable for docs-only tickets. Not blocking.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/67, open

    File Targets

    • [x] README.md -- verified: stale references at lines 3 (shared DB framing), 20 (pal-e-docs FastAPI coexistence), 79 ("No Rails-managed migrations")
    • [x] docs/data-model.md -- verified: stale references at lines 3 ("managed externally -- Rails has no migrations") and 134-137 ("Rails does not own the schema -- table DDL is managed by the FastAPI pal-e-docs service")
    • [x] docs/infrastructure.md -- verified: stale reference at line 61 ("external shared database managed by the pal-e-docs FastAPI service")
    • [x] docs/auth-visibility-architecture.md -- verified: 12+ stale references to pal-e-docs as schema owner, Alembic migrations, shared DB conventions
    • [x] docs/sprint-philosophy.md -- correctly marked as NEW, does not exist yet
    • [x] docs/user-stories.md -- correctly excluded, no stale schema references confirmed

    Repo Placement

    Correct. Issue filed on ldraney/paldocs, all file targets are in paldocs repo. Single-repo change.

    Dependencies

    • Upstream: paldocs#60 (schema ownership transfer) -- merged, confirmed at HEAD
    • Downstream: paldocs#50 (sprint schema) -- open, next_up column. Correctly documented as "informs" not "blocks"
    • No blockers in in_progress column

    Acceptance Criteria

    6 criteria, all verifiable by grep/search. AC6 ("No stale references to Alembic or pal-e-api schema ownership in any doc") provides comprehensive sweep coverage. Test expectations (link resolution, cross-references) are reasonable for a docs-only change.

    Blast Radius

    Minimal. Docs-only change -- no app code, no models, no migrations. Constraints explicitly forbid touching app code. No downstream consumers affected.

    Decomposition Assessment

    5 file targets in 1 repo. 6 acceptance criteria. All docs-only edits. Estimated agent time well under 5 minutes. No decomposition needed.

    Recommendation

    No action needed. Ticket is ready for implementation.

  • Verdict: APPROVED

    Round 2 re-review of board item #1571. Round 1 (review-1571-2026-06-22) found incorrect file targets. The issue body has been updated with all corrections. This review verifies the fixes.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- present, references PR #60 as upstream
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present
    • [x] Context -- thorough, explains both stale docs and sprint philosophy with DORA rationale
    • [x] File Targets -- present, all corrected from round 1
    • [x] Feature Flag -- none, docs-only, appropriate
    • [x] Acceptance Criteria -- 6 criteria, all clear and verifiable
    • [x] Test Expectations -- 2 items, appropriate for docs
    • [x] Constraints -- present
    • [x] Checklist -- present, matches all file targets
    • [x] Related -- present, includes project page, arch note, story, upstream/downstream

    Traceability

    • [x] story:sprint-orchestration label -- present on board item
    • [x] story note verified -- story-paldocs-sprint-orchestration found in project-paldocs user-stories section
    • [x] arch:docs label -- present on board item
    • [ ] arch note MISSING -- [SCOPE] No arch-docs note found in pal-e-docs. Create architecture note arch-docs for the docs component. (Carried from round 1 -- this is external to the issue body and does not block the ticket.)
    • [x] Forgejo issue -- ldraney/paldocs#67, open

    File Targets

    • [x] README.md -- verified: exists at /home/ldraney/paldocs/README.md. Stale references confirmed at lines 3, 12, 19, 20, 42, 79. Line 79 has "No Rails-managed migrations" claim. Line 20 references pal-e-docs FastAPI as coexisting.
    • [x] docs/data-model.md -- verified: exists. Stale references at lines 3, 134-137 (schema managed externally, DDL managed by FastAPI). CORRECTED from round 1 (was docs/shared-database.md).
    • [x] docs/infrastructure.md -- verified: exists. Stale references at lines 61, 64 (external shared database managed by pal-e-docs FastAPI). CORRECTED from round 1 (was docs/architecture.md).
    • [x] docs/auth-visibility-architecture.md -- verified: exists. Stale Alembic references at lines 58, 126. Additional pal-e-docs references throughout (lines 7, 19, 22, 24, 40, 49, 53, 55, 67, etc.). ADDED in round 2 per round 1 recommendation.
    • [x] docs/sprint-philosophy.md -- verified: does NOT exist yet (expected -- ticket says NEW).

    "Files NOT touched" verification: docs/user-stories.md confirmed to have zero stale schema references. Correct to exclude.

    Round 1 Fixes Verified

    • [x] [BODY] docs/shared-database.md corrected to docs/data-model.md -- verified in File Targets and AC2
    • [x] [BODY] docs/architecture.md corrected to docs/infrastructure.md -- verified in File Targets and AC3
    • [x] [BODY] AC2 updated to reference docs/data-model.md -- verified
    • [x] [BODY] AC3 updated to reference docs/infrastructure.md -- verified
    • [x] [BODY] docs/auth-visibility-architecture.md added to File Targets -- verified, with dedicated AC4
    • [ ] [SCOPE] Create arch-docs note -- still outstanding (does not block ticket, see below)

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are in the paldocs repo. Single-repo, docs-only change. No cross-repo work needed.

    Dependencies

    • Upstream (completed): paldocs#49 (schema ownership transfer) -- merged as PR #60. This is what created the stale docs.
    • Downstream: paldocs#50 (sprint schema, in next_up) -- the sprint philosophy doc should inform #50's design. No hard blocker, but logical ordering is correct.
    • No blockers from in_progress items. No items currently in_progress on board-paldocs.

    Acceptance Criteria

    6 acceptance criteria. All verifiable by grep/read:

    • AC1: grep README for pal-e-api DDL references -- verifiable
    • AC2: grep data-model.md for Alembic references -- verifiable
    • AC3: grep infrastructure.md for stale ownership claims -- verifiable
    • AC4: grep auth-visibility-architecture.md for stale pal-e-docs/Alembic references -- verifiable
    • AC5: file existence check + content verification for sprint-philosophy.md -- verifiable
    • AC6: recursive grep across all docs for stale references -- verifiable (catch-all)

    All criteria are machine-verifiable. No ambiguity.

    Blast Radius

    Stale references confirmed in 4 existing files:

    • README.md -- 6 lines with stale references
    • docs/data-model.md -- 3 lines (lines 3, 134, 137)
    • docs/infrastructure.md -- 2 lines (lines 61, 64)
    • docs/auth-visibility-architecture.md -- 2 explicit Alembic lines (58, 126) plus many pal-e-docs references throughout. Note: many pal-e-docs references in this file are about the Keycloak realm name (pal-e-docs realm), which is still valid and should NOT be removed. The agent must distinguish between stale schema ownership references and valid realm name references.

    docs/user-stories.md confirmed clean -- no stale references.

    Decomposition Assessment

    5 file targets in 1 repo. 6 acceptance criteria (marginally above 5-AC guideline). All docs-only, no code changes. Estimated agent work: 3-5 minutes. No decomposition needed. The 6th AC is a catch-all grep that naturally follows from fixing the other 4 files.

    Recommendation

    • [SCOPE] Create architecture note arch-docs in pal-e-docs for the docs component. (Carried from round 1 -- does not block ticket advancement. Can be done in parallel or after.)

    All [BODY] recommendations from round 1 have been addressed. Ticket is ready for implementation.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- present
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present
    • [x] Context -- thorough, explains both stale docs and sprint philosophy
    • [x] File Targets -- present (but inaccurate, see below)
    • [x] Feature Flag -- none, docs-only, appropriate
    • [x] Acceptance Criteria -- 5 criteria, all clear
    • [x] Test Expectations -- 2 items, appropriate for docs
    • [x] Constraints -- present
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:sprint-orchestration label -- present on board item
    • [x] story note verified -- story-paldocs-sprint-orchestration found in project-paldocs user-stories section
    • [ ] arch:docs label -- present on board item
    • [ ] arch note MISSING -- [SCOPE] No arch-docs note found in pal-e-docs. Create architecture note arch-docs for the docs component.
    • [x] Forgejo issue -- ldraney/paldocs#67, open

    File Targets

    • [x] README.md -- verified: exists at /home/ldraney/paldocs/README.md. Line 79 has stale claim "No Rails-managed migrations". Line 20 references pal-e-docs FastAPI as coexisting. Architecture section needs update.
    • [ ] docs/shared-database.md -- ISSUE: File does NOT exist. The actual file is docs/data-model.md which contains the stale shared-database conventions (line 3: "Tables are managed externally", line 134: "table DDL is managed by the FastAPI pal-e-docs service").
    • [ ] docs/architecture.md -- ISSUE: File does NOT exist. The actual files are docs/infrastructure.md (deployment/CI architecture) and docs/auth-visibility-architecture.md (auth/visibility design). Infrastructure.md line 61 has stale reference to "external shared database managed by the pal-e-docs FastAPI service".
    • [x] docs/sprint-philosophy.md -- verified: does NOT exist yet (expected, ticket says NEW).

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are in the paldocs repo. No cross-repo changes needed -- docs-only change.

    Dependencies

    • Upstream (completed): paldocs#49 (schema ownership transfer) -- merged as PR #60. This is what created the stale docs.
    • Downstream: paldocs#50 (sprint schema) is in next_up -- the sprint philosophy doc from this ticket should inform #50's design. No hard blocker, but logical ordering is correct.
    • No blockers from in_progress items.

    Acceptance Criteria

    5 acceptance criteria. All are verifiable by grep/read. However:

    • AC2 says "docs/shared-database.md removed or replaced with docs/database.md" but the file is actually docs/data-model.md. The AC should reference the correct filename.
    • AC3 says "docs/architecture.md reflects Rails as sole owner" but docs/architecture.md does not exist. The stale references are in docs/infrastructure.md and docs/auth-visibility-architecture.md.
    • AC5 "No stale references to Alembic or pal-e-api schema ownership" is good -- grep confirmed stale references in 4 files (data-model.md, infrastructure.md, auth-visibility-architecture.md, README.md).

    Blast Radius

    Stale references found in 4 files total:

    • README.md -- line 79 (no Rails migrations), line 12 (shared database conventions link text), line 20 (pal-e-docs FastAPI coexists)
    • docs/data-model.md -- lines 3, 134-137 (schema managed externally, no DDL)
    • docs/infrastructure.md -- lines 56, 61 (shared database, pal-e-docs FastAPI manages)
    • docs/auth-visibility-architecture.md -- lines 7, 55, 58, 61, 67, 126-128, 196, 206-207 (extensive references to pal-e-docs as schema owner, Alembic migrations)

    The auth-visibility doc has the most stale references (12+ lines) and may need significant rewriting to reflect Rails ownership of schema changes.

    Decomposition Assessment

    4 file targets (README + 3 docs files), 5 acceptance criteria. All within a single repo. Estimated agent work: 3-5 minutes for a docs-only change. No decomposition needed.

    Recommendation

    • [BODY] Fix file path: docs/shared-database.md does not exist. The correct file is docs/data-model.md. Update File Targets section to reference docs/data-model.md instead.
    • [BODY] Fix file path: docs/architecture.md does not exist. The actual files with stale references are docs/infrastructure.md and docs/auth-visibility-architecture.md. Update File Targets to reference both.
    • [BODY] Update AC2: reference docs/data-model.md instead of docs/shared-database.md.
    • [BODY] Update AC3: reference docs/infrastructure.md and docs/auth-visibility-architecture.md instead of docs/architecture.md.
    • [BODY] Add docs/auth-visibility-architecture.md to File Targets -- it has 12+ stale references to pal-e-docs as schema owner / Alembic migrations.
    • [SCOPE] Create architecture note arch-docs in pal-e-docs for the docs component.
  • Verdict: APPROVED

    Round 2 review. AC3 replaced with concrete test fixture assertion, neighbor gem dependency made explicit. All sections present, traceability complete, dependencies correctly documented. Scope is solid for a 3-point ticket.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- decomposed from paldocs#52, depends on #52 and #59
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP layer needs Rails semantic/hybrid search
    • [x] Context -- explains relationship to #52 (embeddings) and #59 (stub)
    • [x] File Targets -- 2 modify/create, 3 do-not-touch boundaries
    • [x] Feature Flag -- none, replaces stub
    • [x] Acceptance Criteria -- 3 criteria, concrete and testable
    • [x] Test Expectations -- unit + 2 integration, run command specified
    • [x] Constraints -- RRF formula, neighbor gem usage, tsvector for keyword
    • [x] Checklist
    • [x] Related -- project, story, arch note, upstream/downstream issues

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:rails-api label -- rails API surface
    • [x] arch note verified -- arch-rails-api note exists in pal-e-docs (confirmed via get_note)
    • [x] Forgejo issue -- ldraney/paldocs#61, open

    File Targets

    • [x] app/services/hybrid_search.rb -- to be created. app/services/ dir does not exist yet but will be created by agent. Standard Rails convention.
    • [x] app/controllers/api/search_controller.rb -- to be created. app/controllers/api/ dir does not exist yet; will be created by upstream #59 (search stub). Ticket correctly states this dependency.
    • [x] Do-not-touch boundaries are clear: block.rb, app/jobs/, db/migrate/ all correctly excluded

    Repo Placement

    OK. Issue filed on ldraney/paldocs, work targets ldraney/paldocs. Single-repo scope.

    Dependencies

    • paldocs#52 (Embedding pipeline) -- in todo (board item #1562). Adds neighbor gem to Gemfile and creates blocks.embedding column. HARD PREREQUISITE. Ticket explicitly declares this dependency.
    • paldocs#59 (Search controller stub) -- in todo (board item #1569). Creates Api::SearchController with 501 stub for semantic endpoint. HARD PREREQUISITE. Ticket explicitly declares this dependency and says it replaces the stub.
    • paldocs#49 (Schema ownership) -- in in_progress (board item #1559). Transfers schema to Rails structure.sql. Transitive dependency via #52.
    • Downstream: paldocs#54 (MCP server) consumes these search endpoints.
    • All dependencies are documented in the Lineage and Constraints sections. Wave ordering must ensure #49 -> #52 -> #59 -> #61.

    Acceptance Criteria

    • [x] AC1: GET /api/search/semantic?q=kubernetes returns blocks ranked by cosine similarity -- agent-verifiable with integration test
    • [x] AC2: GET /api/search/hybrid?q=kubernetes returns RRF-blended results -- agent-verifiable with integration test
    • [x] AC3: HybridSearch.rrf(keyword_results, semantic_results) with known test fixtures returns expected order -- concrete unit test assertion with known inputs, agent-verifiable
    • All 3 criteria are testable. AC3 was improved in Round 2 to specify test fixtures rather than abstract assertion.

    Blast Radius

    Low. This creates new files (service + controller actions) and does not modify existing code. The search controller stub from #59 will be replaced, which is the documented intent. No existing routes, models, or views are affected. The neighbor gem integration (nearest_neighbors call) is scoped to this service class only.

    Decomposition Assessment

    2 file targets in 1 repo. 3 acceptance criteria. Estimated agent work under 5 minutes. No decomposition needed.

    Recommendation

    No action needed. Ticket is well-scoped, dependencies are explicit, AC is concrete and testable. Ready for implementation once upstream tickets #52 and #59 are complete.

  • Verdict: APPROVED

    Round 2 review. Issue body updated with NoteLink/NoteRevision model files, Note.rb tsvector scope, and clarification that no migrations are needed. All issues resolved.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- decomposed from paldocs#51, depends on #49
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP layer needs Rails Notes API
    • [x] Context -- explains consolidation, NoteLink/NoteRevision table existence, tsvector scope need
    • [x] File Targets -- 6 files (4 create, 2 modify), plus explicit "do not touch" list
    • [x] Feature Flag -- "none" (new namespace, no UI impact)
    • [x] Acceptance Criteria -- 6 criteria
    • [x] Test Expectations -- 4 test expectations with run command
    • [x] Constraints -- jbuilder exclusion, JSON shape parity, tsvector raw SQL, no migrations
    • [x] Checklist -- 9 items
    • [x] Related -- project, story, arch note, upstream/sibling/downstream tickets

    Traceability

    • [x] story:consolidation label -- present on board item
    • [x] story note verified -- story-paldocs-consolidation exists in pal-e-docs, listed in project-paldocs user-stories section
    • [x] arch:rails-api label -- present on board item
    • [x] arch note verified -- arch-rails-api note exists (id 2094), confirms Api::NotesController as component
    • [x] Forgejo issue -- ldraney/paldocs#57, state: open

    File Targets

    • [x] app/controllers/api/base_controller.rb -- CREATE. Directory does not exist yet (confirmed). Correct: inherits from ActionController::Base or API, skips CSRF.
    • [x] app/controllers/api/notes_controller.rb -- CREATE. Directory does not exist yet (confirmed). 12 endpoints mapped.
    • [x] app/models/note_link.rb -- CREATE. Confirmed: file does not exist. Tables exist in production DB (imported via pg_dump in #49). No migration needed.
    • [x] app/models/note_revision.rb -- CREATE. Confirmed: file does not exist. Tables exist in production DB. No migration needed.
    • [x] app/models/note.rb -- MODIFY. Confirmed: exists with belongs_to :project, has_many :note_tags, :tags, :board_items, :blocks. Needs has_many :note_links, :note_revisions and tsvector search scope added.
    • [x] config/routes.rb -- MODIFY. Confirmed: exists, currently has resources for projects/notes/boards/board_items. No api namespace yet. Correct target.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, fix is in ldraney/paldocs. Single repo, no cross-repo work.

    Dependencies

    • Upstream: paldocs#49 (schema ownership transfer) -- currently in_progress on board. This ticket depends on #49 because note_links and note_revisions tables must exist via pg_dump import. Documented in issue Lineage section.
    • Siblings: paldocs#58 (boards API), paldocs#59 (projects/repos/tags/search API) -- all in backlog, same wave. No blocking dependency between siblings; they share Api::BaseController which this ticket creates.
    • Downstream: paldocs#54 (new MCP server) -- in todo. Depends on this ticket's API endpoints existing.

    Acceptance Criteria

    All 6 criteria are testable by an agent:

    • Criteria 1-5: standard HTTP request/response assertions in request specs
    • Criterion 6 (JSON shape comparison): verifiable by comparing Rails output to pal-e-docs output. The pal-e-docs FastAPI source is available locally at ~/pal-e-docs for reference.

    Test expectations are concrete: request specs, integration cycle, unit test for search scope, shape comparison. Run command is standard (bundle exec rspec).

    Blast Radius

    • New /api/ namespace is fully isolated from existing HTML routes (/projects, /notes, /boards). No route conflicts.
    • ApplicationController uses ActionController::Base; API base controller will use either ::API or ::Base with CSRF skip. No impact on existing controllers.
    • Adding has_many associations to Note model is additive-only; existing associations unchanged.
    • No jbuilder dependency confirmed (not in Gemfile).
    • Sibling tickets (#58, #59) will reuse Api::BaseController created here -- good foundation.

    Decomposition Assessment

    6 file targets in 1 repo. 6 acceptance criteria (at the 5-rule boundary). However: all work is cohesive (single API namespace, single model domain), the 12 endpoints follow the same CRUD pattern, and the models are trivial (belongs_to + validations). Estimated agent time: 3-4 minutes. No decomposition needed.

    Recommendation

    No action needed. Ticket is well-scoped, all file targets verified, traceability complete, backing notes exist, dependencies documented. Ready for implementation after #49 (schema ownership) completes.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Decomposed from paldocs#52, depends on #52 and #59
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- As the MCP layer...
    • [x] Context -- clear motivation and relationship to consolidation
    • [x] File Targets -- modify and do-not-touch lists present
    • [x] Feature Flag -- "none", appropriate for stub replacement
    • [x] Acceptance Criteria -- 3 criteria present
    • [x] Test Expectations -- unit + integration tests with run command
    • [x] Constraints -- RRF formula, neighbor gem, tsvector dependencies
    • [x] Checklist -- present
    • [x] Related -- project, story, arch, upstream/downstream links

    All required sections for Feature template are present and populated. Template is complete.

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:rails-api label -- rails-api component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails-api. No matching note found in pal-e-docs. Multiple board items reference this arch label (#1567, #1569, #1570) so the note is needed.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/61, open

    File Targets

    • [x] app/services/hybrid_search.rb -- to be created. No app/services/ directory exists yet, but creation is standard Rails convention. OK.
    • [ ] app/controllers/api/search_controller.rb -- ISSUE: This file does not exist yet. It depends on paldocs#59 creating the API namespace and search controller stub first. No app/controllers/api/ directory exists in the current repo. This is correctly documented as a dependency but the agent implementing this ticket must verify #59 is merged before starting.
    • [x] Do-not-touch list is well-defined (block.rb, jobs, migrations)

    Repo Placement

    OK. Issue filed on ldraney/paldocs, work targets ldraney/paldocs. Single-repo scope.

    Dependencies

    • paldocs#52 (Embedding pipeline) -- in todo column, state open. This ticket requires embeddings to exist in blocks.embedding column. BLOCKED until #52 is complete.
    • paldocs#59 (Search controller stub) -- in todo column, state open. This ticket replaces the semantic search stub created by #59. BLOCKED until #59 is complete.
    • neighbor gem -- not yet in Gemfile. Will be added by #52. Dependency is implicit but not explicitly stated in the Constraints section.
    • paldocs#54 (MCP server) -- downstream consumer, in todo column. Not a blocker but should be aware.

    Dependencies are documented in the Lineage section. The blocking relationship is clear. Wave ordering should ensure #52 and #59 complete before this ticket moves to in_progress.

    Acceptance Criteria

    • [x] AC1 (semantic search returns cosine-ranked results) -- testable via integration test
    • [x] AC2 (hybrid search returns RRF-blended results) -- testable via unit + integration test
    • [ ] AC3 ("compare search results to pal-e-docs semantic search, top-5 overlap") -- ISSUE: this is subjective and hard to verify automatically. "Known queries" are not defined. An agent cannot verify "overlap" without a reference dataset. [BODY] Recommend replacing with a concrete assertion, e.g., "When I search for 'keycloak auth' against test fixtures, then the top result contains the keycloak architecture note."

    Blast Radius

    Low. This ticket adds a new service and modifies a controller stub that does not yet exist (created by #59). No existing functionality is changed. The neighbor gem (from #52) and tsvector are database-level concerns already handled by PostgreSQL. No sibling services affected.

    Decomposition Assessment

    2 file targets in 1 repo. 3 acceptance criteria. Estimated agent work under 5 minutes. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails-api for the rails-api component. Multiple board items reference this arch label (#1567, #1569, #1570).
    • [BODY] AC3 is not agent-verifiable as written. Replace "top-5 results overlap for known queries" with a concrete assertion against test fixtures.
    • [BODY] Add explicit note in Constraints that the neighbor gem must be available (added by #52) before this ticket can proceed.
  • Verdict: APPROVED

    Round 2 review. Prior review flagged arch-rails-api as missing; confirmed it exists (created 2026-06-23). All traceability, template, and file target checks pass.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- decomposed from paldocs#51, depends on #49 and #57
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP layer needs Rails board endpoints
    • [x] Context -- sub-ticket 2, 12 tools listed with endpoint mapping
    • [x] File Targets -- 3 files to create/modify, 3 files to avoid
    • [x] Feature Flag -- none (additive API routes)
    • [x] Acceptance Criteria -- 5 criteria
    • [x] Test Expectations -- request specs, integration, response shape comparison
    • [x] Constraints -- as_json, no jbuilder, match pal-e-docs shape
    • [x] Checklist -- 7 items
    • [x] Related -- project, story, arch, upstream/sibling/downstream

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation note exists, listed on project-paldocs user-stories section
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:rails-api label -- arch-rails-api note exists (architecture type, active status, project paldocs)
    • [x] arch note verified -- arch-rails-api lists Api::BoardsController and Api::BoardItemsController as components
    • [x] Forgejo issue -- paldocs#58, open

    File Targets

    • [x] app/controllers/api/boards_controller.rb -- to be created. api/ directory does not exist yet (depends on #57 creating it). Correct.
    • [x] app/controllers/api/board_items_controller.rb -- to be created. Same dependency. Correct.
    • [x] config/routes.rb -- exists. Currently no api namespace; #57 will add it. Correct.
    • [x] app/controllers/api/base_controller.rb -- listed as "should NOT touch." Does not exist yet; #57 will create it. Correct.
    • [x] app/models/ -- listed as "should NOT touch." Models already exist (BoardItem model confirmed; Board is a Note, not a separate model). Correct.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, work targets ldraney/paldocs. Single repo.

    Dependencies

    • paldocs#49 (schema ownership transfer) -- in_progress on board. Must complete first so structure.sql exists.
    • paldocs#57 (API namespace + Notes controller) -- backlog on board. Must complete first to create api/ directory, base_controller, and api namespace in routes.
    • Both dependencies are correctly documented in the issue Lineage and Context sections.
    • Sibling: paldocs#59 (projects/repos/tags/search API) -- independent, no blocking relationship.
    • Downstream: paldocs#54 (new MCP server) -- blocked by this ticket. Documented.

    Acceptance Criteria

    5 criteria, all verifiable by an agent. Each follows "When MCP calls X, then Y" pattern. Response shape comparison criterion is testable by comparing JSON output against pal-e-docs. Run command: bundle exec rspec. Solid.

    Blast Radius

    • No existing API controllers to conflict with (api/ directory does not exist yet).
    • Existing UI routes for boards and board_items (show, update) are in the non-namespaced routes -- no collision risk with /api/ namespace.
    • BoardItem model uses Note as board (class_name: "Note", foreign_key: :board_note_id). The implementing agent must query Note records for board CRUD, not a Board model. The arch note documents this correctly.
    • Board sync logic from pal-e-docs needs replication -- agent should reference pal-e-docs source for sync behavior.

    Decomposition Assessment

    3 file targets in 1 repo. 5 acceptance criteria. Estimated agent work under 5 minutes. No decomposition needed.

    Recommendation

    No action needed. Ticket is ready for implementation once dependencies (#49, #57) are complete.

  • Verdict: APPROVED

    Round 2 review of board item #1569 (paldocs#59). Issue body updated from Round 1: removed search_notes overlap with #57, added explicit AC for all 8 endpoints, arch-rails-api note confirmed to exist. Scope is clean and agent-ready.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- decomposed from paldocs#51
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP layer needs Rails endpoints
    • [x] Context -- sub-ticket 3, search_notes overlap clarified
    • [x] File Targets -- 4 controllers + routes.rb
    • [x] Feature Flag -- none (additive API routes)
    • [x] Acceptance Criteria -- 8 items, one per endpoint
    • [x] Test Expectations -- request specs + integration + stub verification
    • [x] Constraints -- JSON format, no jbuilder, 501 stub
    • [x] Checklist -- 9 items
    • [x] Related -- project, story, arch, upstream/sibling/downstream all linked

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:rails-api label -- arch-rails-api
    • [x] arch note verified -- arch-rails-api note exists in pal-e-docs (note id 2094)
    • [x] Forgejo issue -- paldocs#59, state: open

    File Targets

    • [x] app/controllers/api/projects_controller.rb -- does not exist yet (will be created). Parent dir app/controllers/api/ also does not exist yet, will be created by #57 (API namespace dependency)
    • [x] app/controllers/api/repos_controller.rb -- does not exist yet (will be created)
    • [x] app/controllers/api/tags_controller.rb -- does not exist yet (will be created)
    • [x] app/controllers/api/search_controller.rb -- does not exist yet (will be created)
    • [x] config/routes.rb -- exists, currently has no api namespace. Will be modified (api namespace added by #57 first)
    • [x] Models verified: Project, Repo, Tag models all exist in app/models/
    • [x] "Do not touch" boundaries clear: base_controller.rb (#57), notes_controller.rb (#57), existing UI controllers, models

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all work targets paldocs. Single-repo change.

    Dependencies

    • paldocs#49 (Schema ownership transfer) -- in_progress on board. Required for model scopes. Board item #1559.
    • paldocs#57 (API namespace + Notes controller) -- backlog on board. Creates the api/ directory, base_controller.rb, and api namespace in routes. Board item #1567. This ticket MUST complete before #59 can start.
    • paldocs#58 (Boards API) -- sibling, no ordering dependency. Board item #1568.
    • paldocs#61 (Hybrid search) -- downstream, fills the semantic stub. Board item #1570.
    • paldocs#54 (Paldocs MCP server) -- downstream consumer of these endpoints. Board item #1564.

    Dependencies are well-documented in the issue body (Lineage + Related sections).

    Acceptance Criteria

    All 8 AC are verifiable by an agent via curl/rspec. Each criterion maps to exactly one endpoint. The 501 stub criterion for semantic search is testable (check status code + message). No ambiguous or untestable criteria.

    Blast Radius

    Low. All changes are additive API routes under /api/ namespace. No existing UI controllers or views are modified. The "Do not touch" section explicitly protects existing code. The semantic search stub (501) is a safe placeholder -- downstream #61 fills it later.

    The arch-rails-api note currently shows ProjectsController as "GET /api/projects" only. This ticket expands it to include POST and DELETE. The arch note should be updated after implementation to reflect the full CRUD surface -- but this is normal post-merge documentation, not a scope issue.

    Decomposition Assessment

    4 file targets (controllers) + 1 routes file, all in 1 repo. 8 AC. Estimated agent time: ~3-4 minutes. The controllers are small (1-3 endpoints each), follow the same pattern, and are all in the same directory. No decomposition needed despite 8 AC -- the work is repetitive boilerplate, not complex logic.

    Recommendation

    • [BODY] Update Forgejo issue title from "9 endpoints" to "8 endpoints" to match the body (search_notes was removed in Round 2 update). Current title: "API Projects, Repos, Tags, Search controllers -- 9 endpoints for MCP"
    • [LABEL] Board item #1569 title also says "9 endpoints" -- update to match

    These are cosmetic title sync issues only. Scope is APPROVED as-is.

  • Verdict: APPROVED

    Round 2 review. Ticket is well-scoped after refinement. Missing file targets from Round 1 were added (commands, tests, hooks). PAL_E_API_URL correctly deferred. Minor issues remain but are informational, not blockers.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#54
    • [x] Repo -- ldraney/claude-custom (primary), ldraney/paldocs (cleanup)
    • [x] User Story -- As the Overseer...
    • [x] Context -- thorough breakdown of reference categories and counts
    • [x] File Targets -- comprehensive list with should/should-not-touch sections
    • [x] Feature Flag -- none (clean swap)
    • [x] Acceptance Criteria -- 6 criteria, all testable
    • [x] Test Expectations -- automated and manual, includes grep commands
    • [x] Constraints -- dependency ordering, human gate, rollback plan
    • [x] Checklist -- 16-item checklist
    • [x] Related -- project, story, arch, upstream references

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation
    • [x] story note verified -- found in project-paldocs user-stories section
    • [x] arch:mcp label -- architecture component MCP
    • [ ] arch note MISSING -- search for "arch-mcp" returned zero results. [SCOPE] Create architecture note arch-mcp for MCP component
    • [x] Forgejo issue -- ldraney/paldocs#55, open

    File Targets

    • [x] settings.json -- verified: 4 mcp__pal-e-docs matcher refs
    • [x] settings.local.json -- verified: 1 "pal-e-docs" ref in enabledMcpjsonServers
    • [x] agents/overseer.md -- verified: 1 mcp__pal-e-docs + 10 name refs (ticket claims 11 total, correct)
    • [x] agents/qa.md -- verified: has mcp__pal-e-docs refs + 4 name refs
    • [x] hooks/*.sh -- verified: 8 hooks with mcp__pal-e-docs refs, 16 hooks total with any pal-e-docs ref
    • [x] hooks/boards-config.sh -- verified: "board-pal-e-docs" slug ref
    • [x] hooks/cleanup-worktrees.sh -- verified: 3 repo path refs ($HOME/pal-e-docs etc)
    • [x] hooks/stop-doc-checkin.sh -- verified: 1 mcp ref + 2 name refs
    • [x] skills/*.md -- verified: 8 skill files with mcp__pal-e-docs refs
    • [x] commands/update-docs.md -- verified: 7 mcp__pal-e-docs refs
    • [x] docs/*.md -- verified: 8 doc files with pal-e-docs refs
    • [x] tests/test_check_board_advance.sh -- verified: 33 mcp__pal-e-docs refs (ticket says ~50, actual is 33+2 name refs = 35 total)
    • [x] ~/.mcp.json -- verified: "pal-e-docs" entry exists

    Informational -- test fixture count: Ticket says "~50 test fixture references" for test_check_board_advance.sh. Actual count is 33 mcp__pal-e-docs + 2 name refs = 35 total. Not a blocker.

    Informational -- hook count: Ticket says "15 hook scripts" for MCP tool name references. Actual: 8 hooks with mcp__pal-e-docs tool refs, 16 hooks total with any pal-e-docs reference (including name-only and PAL_E_API_URL). The hooks/*.sh glob instruction says "grep for mcp__pal-e-docs" which would miss 4 hooks with name-only refs (boards-config.sh, check-agent-spawn.sh, cleanup-worktrees.sh, inject-subagent-context.sh). However, 3 of these 4 are already called out as specific File Targets, and inject-subagent-context.sh is covered by the glob. Not a blocker.

    Informational -- implicit targets covered by globs/intent:

    • agents/dev.md -- 2 name refs. Covered: inject-subagent-context.sh generates the context strings containing these refs, so updating the hook updates dev.md's runtime behavior. The static file itself also needs updating.
    • tests/test_check_note_template.sh -- 23 mcp__pal-e-docs refs. NOT listed explicitly but covered by tests/ intent if agent greps broadly.
    • hooks/inject-subagent-context.sh -- 3 name refs. Covered by hooks/*.sh glob.
    • hooks/check-agent-spawn.sh -- 1 name ref. Covered by hooks/*.sh glob.
    • README.md -- 1 pal-e-docs ref. Covered by docs intent.
    • spikes/133-penny-mcp-inventory.md -- 1 ref. Historical spike, may or may not need updating.

    Repo Placement

    OK. Ticket correctly identifies ldraney/claude-custom as primary and ldraney/paldocs for cleanup. Issue filed on paldocs is correct -- consolidation story lives there. Cross-repo concern tracked on consumer side is fine.

    Dependencies

    • Upstream blocker: paldocs#54 (Paldocs MCP server) -- board item #1564, currently in backlog. Must be done first. Correctly documented.
    • Upstream for PAL_E_API_URL: paldocs#57-#59 (API endpoints) -- correctly deferred.
    • No downstream blockers on the board for this item.
    • Related parallel work: paldocs#52 (embeddings), #61 (hybrid search) -- not blocking.

    Acceptance Criteria

    6 ACs, all agent-verifiable:

    • AC1: grep mcp__pal-e-docs returns zero -- automatable, clear.
    • AC2: grep pal-e-docs in non-git non-memory files shows only PAL_E_API_URL refs remaining -- automatable. Note: 87 non-mcp, non-API_URL name references exist today across 30 files. Agent must rename ALL "pal-e-docs" strings (not just mcp__ prefix) except PAL_E_API_URL lines. The AC is correct and comprehensive.
    • AC3: session shows only paldocs MCP server -- manual verification.
    • AC4: hooks match mcp__paldocs__* tool names -- verifiable via settings.json grep.
    • AC5: ~/.mcp.json has no pal-e-docs entry -- automatable.
    • AC6: test_check_board_advance.sh passes -- automatable. Note: test_check_note_template.sh should also pass but is not mentioned in AC. Covered by general "no regressions" in checklist.

    Blast Radius

    • 43 files confirmed. Grep output matches ticket claim exactly.
    • Mixed-reference hooks (6): check-board-advance.sh, check-issue-template.sh, check-note-template.sh, check-pr-template.sh, gate-validation-done.sh, session-start-context.sh all have BOTH rename-target refs AND PAL_E_API_URL refs. Agent must use line-level precision, not whole-file find-replace.
    • Highest complexity: session-start-context.sh (16 refs: 2 mcp + 7 name + 7 PAL_E_API_URL), block-docs-writes.sh (23 refs: 20 mcp + 3 name).
    • No sibling services affected outside claude-custom.

    Decomposition Assessment

    43 files across 1 primary repo + ~/.mcp.json. 6 ACs. The work is fundamentally a find-and-replace with careful exclusions (PAL_E_API_URL lines). Despite high file count, this is a single conceptual operation executable in one agent pass via grep + Edit. Estimated agent time: ~5 minutes. Borderline on the 5-minute rule but decomposition would add coordination overhead for what is a rename operation. No decomposition needed.

    Recommendation

    [SCOPE] Create architecture note arch-mcp for the MCP component. The arch:mcp label is used on board items #1564 and #1565 but no backing note exists in pal-e-docs.

    All other aspects are solid. Ticket is APPROVED for backlog-to-todo after arch note is created.

  • Verdict: APPROVED

    Round 2 re-review. Narrowed from 8pts to 5pts by splitting search endpoints to paldocs#61. All prior NEEDS_REFINEMENT issues resolved: story note exists, arch note exists, decomposition completed. Scope is now focused on embedding pipeline only.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#49, search split to #61
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- well-formed "As the platform / I need / So that"
    • [x] Context -- thorough: current Python pipeline, Rails replacement architecture, coexistence strategy with race-avoidance plan
    • [x] File Targets -- 5 files (3 new, 2 modified), plus "do not touch" entries with rationale
    • [x] Feature Flag -- "none" with justification (replaces existing functionality with coexistence strategy)
    • [x] Acceptance Criteria -- 4 ACs, all in testable "When/Then" format (down from 7 pre-split)
    • [x] Test Expectations -- 3 unit tests plus run command
    • [x] Constraints -- Ollama endpoint, model (qwen3-embedding:4b), dimensions (2560), block granularity, mermaid skip, queue depth (~5600 blocks), Solid Queue
    • [x] Checklist -- 7 items
    • [x] Related -- project, story, arch note, upstream/downstream issues

    All required sections for the Feature template are present and well-populated.

    Traceability

    • [x] story:consolidation label -- present on board item
    • [x] story note verified -- story-paldocs-consolidation exists in pal-e-docs, entry found in project-paldocs user-stories section
    • [x] arch:rails-jobs label -- present on board item
    • [x] arch note verified -- arch-rails-jobs exists in pal-e-docs (note ID 2095), titled "Architecture: Rails Jobs -- Solid Queue embedding pipeline". Contains sequence diagram matching the issue's described flow.
    • [x] Forgejo issue -- ldraney/paldocs#52, state: open

    Traceability triangle is complete.

    File Targets

    • [x] app/models/block.rb -- verified: file exists (5 lines, belongs_to :note). Ready for after_save callback + has_neighbors :embedding declaration.
    • [x] app/jobs/embed_block_job.rb -- does not exist yet (will be created). app/jobs/ directory exists with application_job.rb. Solid Queue is configured (Gemfile line 26, puma.rb, queue.yml).
    • [x] app/services/ollama_client.rb -- does not exist yet (will be created). NOTE: app/services/ directory does NOT exist -- agent must create it. Issue body now notes this.
    • [x] config/initializers/ollama.rb -- does not exist yet (will be created). config/initializers/ directory exists.
    • [x] Gemfile -- verified: exists. Neighbor gem is NOT present yet. Issue correctly notes "(may already be added in paldocs#49)". Since #49 is in_progress, this ticket should own adding neighbor if #49 doesn't.

    All 5 file targets verified. No search controller or hybrid search files -- correctly moved to #61.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are in paldocs. Single-repo scope. No mismatch.

    Dependencies

    • BLOCKER: paldocs#49 (schema ownership transfer) -- board item #1559, currently in_progress. This ticket explicitly depends on #49 for structure.sql with the vector column, pgvector extension, and potentially the neighbor gem. Until #49 merges, there is no Rails-owned schema with the embedding column. Dependency is documented in the issue.
    • Downstream: paldocs#61 (hybrid search) -- board item #1570, backlog. Depends on this ticket for embeddings to exist. Correctly split out.
    • No other blocking dependencies. Parallel items (sprint schema #50, sprint UI #56) do not conflict.

    Dependencies are well-documented in the issue's Lineage and Related sections. Blocker is acknowledged and tracked.

    Acceptance Criteria

    4 ACs, all in testable "When/Then" format:

    • AC1 (job enqueued on block save) -- unit testable via ActiveJob test adapter
    • AC2 (Ollama call + vector write) -- testable with mocked HTTP. Ollama URL confirmed matching existing Python worker.
    • AC3 (mermaid skip) -- unit testable, clear criterion with embedding_status='skipped'
    • AC4 (retry with exponential backoff, 3 attempts, dead letter) -- testable with connection stubbing, specific retry count given

    All ACs are automatable. Count is within the 5 AC threshold. Previous vague AC7 (result equivalence) has been moved to #61.

    Blast Radius

    • Coexistence with Python embedding worker is explicitly addressed. Strategy is sound: deploy Rails job first, verify, then scale Python worker to 0.
    • Both systems write to the same blocks.embedding column -- last-writer-wins is documented and accepted.
    • No other services share the embedding column -- blast radius is contained.
    • No search endpoints in this ticket -- blast radius further reduced by the split.

    Decomposition Assessment

    No decomposition needed.

    • File targets: 5 files (3 new, 2 modified) in 1 repo
    • Acceptance criteria: 4 ACs -- within the 5 AC threshold
    • Test expectations: 3 unit tests
    • Estimated agent work: Under 5 minutes. Create Ollama HTTP client, embedding job with retry logic, model callback with mermaid skip, initializer. Straightforward Rails patterns.

    The decomposition from the prior review has been completed: search split to #61 (3pts). This ticket (5pts) is now right-sized for a single agent pass.

    Recommendation

    No action needed. All prior NEEDS_REFINEMENT items have been resolved:

    • Search controller and hybrid search moved to #61 -- resolves [BODY] and [DECOMPOSE] items
    • Story note and arch note exist -- resolves [SCOPE] items from round 1
    • Issue body is clean and focused on embedding pipeline only

    Minor observation (not blocking): app/services/ directory does not exist. The issue's File Targets section notes this. Agent will need to mkdir -p app/services before creating ollama_client.rb.

  • Verdict: APPROVED

    Round 2 re-review. Both issues from review-1564-2026-06-22 have been resolved.

    Previous Issues -- Resolution

    • [x] arch:infra label changed to arch:mcp -- confirmed on board item #1564 (labels now type:feature,story:consolidation,arch:mcp). Architecture note arch-mcp created in pal-e-docs with overview, tool surface (36 tools, 7 groups), and transition strategy.
    • [x] AC5 deferred pending #52 -- issue body now reads "deferred verification -- not blocking this ticket" in AC5 and Constraints section says "Semantic search verification deferred until paldocs#52 lands."

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Depends on paldocs#57, #58, #59 (API endpoints). Updated from #51 to split issues.
    • [x] Repo -- ldraney/paldocs (or new ldraney/paldocs-mcp)
    • [x] User Story -- present, well-formed
    • [x] Context -- present, good rationale
    • [x] File Targets -- present (new MCP code, ~/.mcp.json, with do-not-touch list)
    • [x] Feature Flag -- none (additive, correct)
    • [x] Acceptance Criteria -- 5 criteria (AC5 deferred)
    • [x] Test Expectations -- present
    • [x] Constraints -- present, includes deferred semantic search note
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:consolidation label -- present on board item
    • [x] story note verified -- story-paldocs-consolidation exists (user-story type, project paldocs), listed in project-paldocs user-stories section
    • [x] arch:mcp label -- present on board item (changed from arch:infra per round 1 recommendation)
    • [x] arch note verified -- arch-mcp exists in pal-e-docs (doc type, project paldocs, tags: architecture, paldocs, mcp)
    • [x] Forgejo issue -- ldraney/paldocs#54, open

    File Targets

    • [x] ~/.mcp.json -- verified: exists, lists 8 servers including "pal-e-docs". Adding "paldocs" alongside is correct.
    • [x] ~/pal-e-mcp/ -- correctly listed as do-NOT-touch. Verified: exists with src/, tests/, pyproject.toml.
    • [x] ~/claude-custom/settings.json -- correctly listed as do-NOT-touch (migrated in #55).
    • [x] New MCP server code -- TBD repo placement acceptable as design decision.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, acknowledges server may live in new ldraney/paldocs-mcp repo. Either placement is valid.

    Dependencies

    • paldocs#57 (Notes API, 12 endpoints) -- OPEN, backlog, board item #1567. Hard dependency.
    • paldocs#58 (Boards + Board Items API, 12 endpoints) -- OPEN, backlog, board item #1568. Hard dependency.
    • paldocs#59 (Projects/Repos/Tags/Search API, 9 endpoints) -- OPEN, backlog, board item #1569. Hard dependency.
    • paldocs#52 (Embedding pipeline) -- OPEN, backlog, board item #1562. Required for AC5 (deferred). Not blocking.
    • paldocs#49 (Schema ownership) -- in_progress, board item #1559. Indirect dependency (must complete before API endpoints).
    • paldocs#55 (Migrate tool references) -- OPEN, backlog, board item #1565. Downstream (depends on this ticket).

    Dependency chain is correct: wave:0 (#49 schema) -> wave:1 (#57, #58, #59 API) -> wave:2 (this #54 MCP) -> wave:3 (#55 migration).

    Acceptance Criteria

    • [x] AC1 (MCP server initializes) -- verifiable
    • [x] AC2 (36 tools match old server) -- verifiable
    • [x] AC3 (no tool name conflicts) -- verifiable, mcp__paldocs__* vs mcp__pal-e-docs__*
    • [x] AC4 (CRUD operations) -- verifiable via smoke test
    • [x] AC5 (semantic search) -- correctly deferred pending #52. Not blocking.

    Blast Radius

    Low. Purely additive -- new server alongside old. No existing tool names affected. Risk limited to naming collisions, addressed by AC3 and prefix convention.

    Decomposition Assessment

    1-2 file locations, single repo, ~200-300 lines thin proxy. 5 AC (one deferred). No decomposition needed.

    Recommendation

    No action needed. All round 1 issues resolved. Ticket is ready for implementation.

  • Verdict: READY

    Round 2 re-review. Round 1 flagged arch-rails-views as missing. Confirmed it exists (id 1839, note_type architecture, status active). All prior checks still pass. Ticket is ready for todo.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#50
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- well-formed As/Want/So-that
    • [x] Context -- explains sprint workflow, relationship to #50
    • [x] File Targets -- 6 files to create/modify, 3 exclusions
    • [x] Feature Flag -- none (correct, new routes only)
    • [x] Acceptance Criteria -- 6 criteria, all agent-verifiable
    • [x] Test Expectations -- 4 tests + run command
    • [x] Constraints -- dependency, Hotwire pattern, functional-first
    • [x] Checklist -- present
    • [x] Related -- project, story, arch note, upstream/downstream

    Traceability

    • [x] story:sprint-orchestration label -- present on board item
    • [x] story note verified -- story-paldocs-sprint-orchestration exists in pal-e-docs (id 2093) and is listed in project-paldocs user-stories section
    • [x] arch:rails-views label -- present on board item
    • [x] arch note verified -- arch-rails-views exists in pal-e-docs (id 1839, note_type architecture, active). Round 1 incorrectly flagged this as missing.
    • [x] Forgejo issue -- ldraney/paldocs#56, open

    File Targets

    • [x] app/controllers/sprint_planning_controller.rb -- does not exist yet (correct, new file to create)
    • [x] app/views/sprint_planning/index.html.erb -- does not exist yet (correct, new file)
    • [x] app/views/sprint_planning/show.html.erb -- does not exist yet (correct, new file)
    • [x] app/views/sprint_planning/_wave.html.erb -- does not exist yet (correct, new partial)
    • [x] config/routes.rb -- exists, verified. Currently has resources for projects, notes, boards, board_items. Sprint planning routes would be added here.
    • [x] app/views/layouts/ -- exists, contains application.html.erb. Navigation currently has only brand link. Sprint planning nav link would go here.
    • [x] Exclusions verified -- db/migrate/, app/models/, db/structure.sql correctly excluded (belong to paldocs#50)

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are in the paldocs repo. Single-repo scope.

    Dependencies

    • Depends on paldocs#50 (Sprint schema -- migrations + models). Board item #1560, currently in todo column. This ticket cannot start until #50 merges (needs Sprint, SprintProject models and board_items.sprint_id/wave_number columns).
    • paldocs#50 itself may depend on paldocs#49 (Schema ownership transfer, board item #1559, in_progress). Dependency chain is documented and logical.
    • No other board items block this ticket.

    Acceptance Criteria

    All 6 criteria are agent-verifiable: navigation link presence, sprint CRUD, board item display, side-by-side view, wave staging with sprint_id/wave_number, wave grouping with points. Test commands are real (bundle exec rspec). No missing criteria for the stated scope.

    Blast Radius

    Low. This ticket adds new routes and views only. No modification to existing controllers or views except adding a nav link to the layout. Existing board/project views are untouched. The new controller follows the same pattern as BoardsController (Hotwire, turbo frames, stimulus controllers).

    Decomposition Assessment

    6 file targets in 1 repo. 6 acceptance criteria. Estimated agent work under 5 minutes -- straightforward Rails controller + ERB views following established patterns. No decomposition needed.

    Recommendation

    No action needed. Ticket is ready for todo.

  • Verdict: NEEDS_REFINEMENT

    Re-review (rev 2, 2026-06-22). Prior SCOPE items for story note and arch note are now resolved. Issue body has been expanded. Decomposition still required.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#49, part of consolidation
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- well-formed "As the platform / I need / So that"
    • [x] Context -- thorough: describes current Python pipeline, Rails replacement architecture, coexistence strategy with race-avoidance plan
    • [x] File Targets -- 7 files to create/modify, 2 "do not touch" entries, all with descriptions
    • [x] Feature Flag -- "none" with justification (replaces existing functionality with coexistence strategy)
    • [x] Acceptance Criteria -- 7 ACs, all in testable "When/Then" format
    • [x] Test Expectations -- 5 unit/integration tests plus run command
    • [x] Constraints -- Ollama endpoint, model (qwen3-embedding:4b), dimensions (2560), block granularity, mermaid skip, queue depth (~5600 blocks), Solid Queue
    • [x] Checklist -- 8 items
    • [x] Related -- project, story, arch note, upstream/parallel/downstream issues all listed

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

    Traceability

    • [x] story:consolidation label -- present on board item
    • [x] story note verified -- story-paldocs-consolidation exists in pal-e-docs (note ID 2092), titled "Story: Consolidation -- absorb pal-e-docs into Rails monolith". Entry found in project-paldocs user-stories section.
    • [x] arch:rails-jobs label -- present on board item
    • [x] arch note verified -- arch-rails-jobs exists in pal-e-docs (note ID 2095), titled "Architecture: Rails Jobs -- Solid Queue embedding pipeline". Contains sequence diagram matching the issue's described flow.
    • [x] Forgejo issue -- ldraney/paldocs#52, state: open

    Traceability triangle is complete. Prior review's [SCOPE] items for missing story and arch notes have been resolved.

    File Targets

    • [x] app/models/block.rb -- verified: file exists (5 lines, belongs_to :note). Ready for after_save callback + has_neighbors :embedding declaration.
    • [x] app/jobs/embed_block_job.rb -- does not exist yet (will be created). app/jobs/ directory exists with application_job.rb. Solid Queue is configured (Gemfile, puma.rb, queue.yml).
    • [x] app/services/ollama_client.rb -- does not exist yet (will be created). NOTE: app/services/ directory does NOT exist yet -- agent must create it.
    • [x] app/services/hybrid_search.rb -- does not exist yet (will be created). Same directory note.
    • [ ] app/controllers/api/search_controller.rb -- ISSUE: app/controllers/api/ directory does not exist. Issue body says "replaces stub from #51" but paldocs#51 (API endpoints) is still open and not started -- no API namespace, no base API controller, no API routes exist. This target either depends on #51 completing first or the agent must also create the entire API namespace and routing.
    • [x] config/initializers/ollama.rb -- does not exist yet (will be created). config/initializers/ directory exists.
    • [x] Gemfile -- verified: exists. Neighbor gem is NOT present yet. Issue correctly notes "(may already be added in paldocs#49)". Since #49 is in_progress, this ticket should own adding neighbor if #49 doesn't.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are in paldocs. Single-repo scope. No mismatch.

    Dependencies

    • BLOCKER: paldocs#49 (schema ownership transfer) -- board item #1559, currently in_progress. This ticket explicitly depends on #49 for: structure.sql with the vector column, pgvector extension, and potentially the neighbor gem. Until #49 merges, there is no Rails-owned schema with the embedding column. Dependency is documented in the issue.
    • Soft dependency: paldocs#51 (API endpoints) -- open, not started. The search controller file target references "replaces stub from #51", implying #51 creates the API namespace and base controller first. If #51 is not done, this ticket must also create the api/ controller namespace and routing infrastructure.
    • Parallel (no conflict): paldocs#50 (sprint schema, wave:1), paldocs#56 (sprint UI)
    • Downstream: paldocs#54 (new MCP server, wave:3) depends on this ticket's search endpoints

    Dependencies are well-documented in the issue's Lineage and Related sections.

    Acceptance Criteria

    All 7 ACs are in testable "When/Then" format:

    • AC1 (job enqueued on block save) -- unit testable via ActiveJob test adapter
    • AC2 (Ollama call + vector write) -- testable with mocked HTTP, Ollama URL confirmed matching existing Python worker (http://ollama.ollama.svc.cluster.local:11434/api/embed)
    • AC3 (mermaid skip) -- unit testable, clear criterion
    • AC4 (semantic search endpoint) -- integration testable
    • AC5 (hybrid search with RRF) -- integration testable, well-specified
    • AC6 (retry with exponential backoff, 3 attempts, dead letter) -- testable with connection stubbing, specific retry count given
    • AC7 (result equivalence with pal-e-docs) -- Vague. "Results are equivalent" lacks a concrete threshold or test dataset. However, since the same model (qwen3-embedding:4b) and dimensions (2560) are used, functional equivalence is reasonable to assume. Suggest rewording to "hybrid search returns fused keyword+semantic results for the same queries that pal-e-docs semantic_search serves."

    Blast Radius

    • Coexistence with Python embedding worker is explicitly addressed in the issue body. Strategy is sound: deploy Rails job first, verify, then scale Python worker to 0. PostgreSQL LISTEN/NOTIFY trigger stays (no conflict with ActiveRecord callbacks).
    • Ollama URL confirmed matching existing Python worker: http://ollama.ollama.svc.cluster.local:11434 (verified in pal-e-docs/src/pal_e_docs/config.py).
    • Both systems write to the same blocks.embedding column -- last-writer-wins is documented and accepted.
    • MCP server (pal-e-docs) currently serves semantic_search. During transition, search results could diverge if both systems are live, but this is inherent to the migration and acceptable.
    • No other services share the embedding column -- blast radius is contained to paldocs + pal-e-docs.

    Decomposition Assessment

    NEEDS DECOMPOSITION -- route to skill-decompose-ticket

    • File targets: 7 files (5 new, 2 modified) in 1 repo
    • Acceptance criteria: 7 ACs -- exceeds the 5 AC threshold
    • Test expectations: 5 distinct test items
    • Estimated agent work: Well over 5 minutes. Must create: Ollama HTTP client with error handling, embedding job with retry/dead-letter logic, model callbacks with mermaid skip, RRF hybrid search algorithm, search controller with routing, initializer with configuration.
    • Natural decomposition into 2 sub-tickets:
      1. Embedding pipeline (AC1-3, AC6): app/services/ollama_client.rb, config/initializers/ollama.rb, app/jobs/embed_block_job.rb, app/models/block.rb (callback + neighbor), Gemfile (neighbor gem if not from #49)
      2. Search endpoints (AC4-5, AC7): app/services/hybrid_search.rb, app/controllers/api/search_controller.rb (+ API namespace/routes). Note: depends on sub-ticket 1 for embeddings to exist, and soft-depends on #51 for API namespace.

    Recommendation

    • [BODY] Fix search controller reference: "replaces stub from #51" is misleading since #51 is not started and no API namespace exists. Clarify that this ticket (or its sub-ticket) must create the app/controllers/api/ directory and routing if #51 is not done first.
    • [BODY] Clarify AC7 (result equivalence): "results are equivalent" is not automatable. Suggest rewording to "hybrid search returns fused keyword+semantic results for known test content."
    • [BODY] Note that app/services/ directory does not exist and must be created by the agent.
    • [DECOMPOSE] 7 ACs across 7 file targets, 3 distinct subsystems (Ollama client, embedding pipeline, search/RRF), estimated well over 5 min agent work. Route to skill-decompose-ticket. Natural split: (1) embedding pipeline -- Ollama client + job + model callback + initializer, (2) search endpoints -- hybrid search service + controller + routing.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Depends on paldocs#51, replaces paldocs#53 (closed)
    • [x] Repo -- ldraney/paldocs (or new ldraney/paldocs-mcp)
    • [x] User Story -- present, well-formed
    • [x] Context -- present, good rationale for new MCP vs repointing
    • [x] File Targets -- present (new MCP server code, ~/.mcp.json)
    • [x] Feature Flag -- present (none, additive)
    • [x] Acceptance Criteria -- 5 criteria
    • [x] Test Expectations -- present
    • [x] Constraints -- present
    • [x] Checklist -- present
    • [x] Related -- present

    All template sections present.

    Traceability

    • [x] story:consolidation label -- present on board item
    • [x] story note verified -- story-paldocs-consolidation exists in pal-e-docs, listed in project-paldocs user-stories section
    • [ ] arch:infra label -- TOO GENERIC. No arch-infra note exists. Multiple previous reviews (review-1552, review-1432, review-1563) have flagged arch:infra as a non-existent catch-all. The story note references arch-rails-api as the relevant architecture component for MCP-facing work. [LABEL] Change arch:infra to arch:mcp or create arch-mcp note for the MCP server layer.
    • [x] Forgejo issue -- ldraney/paldocs#54, open

    File Targets

    • [x] ~/.mcp.json -- verified: exists, currently lists "pal-e-docs" server. Adding "paldocs" entry is correct.
    • [x] ~/pal-e-mcp/ -- correctly listed as "do NOT touch". Verified: exists at ~/pal-e-mcp/ with 36 tools across 7 modules.
    • [x] ~/claude-custom/settings.json -- correctly listed as "do NOT touch" (tool references migrated in #55).
    • [x] New MCP server code -- TBD repo placement is acceptable (design decision). Repo ldraney/paldocs-mcp does not yet exist on Forgejo.

    Note: The issue says "36 MCP tools" -- verified accurate: boards (12), notes (9), blocks (6), projects (3), repos (3), links (2), tags (1) = 36 tools.

    Repo Placement

    Issue is filed on ldraney/paldocs but acknowledges the server may live in a new ldraney/paldocs-mcp repo. This is acceptable -- the decision depends on whether the MCP server is a subdirectory of the Rails app or a standalone project. Either way, the Forgejo issue is correctly placed on paldocs since it is the parent project.

    Dependencies

    • paldocs#51 (API endpoints) -- OPEN, in backlog on board-paldocs. This is the hard dependency: the MCP server cannot proxy to Rails API endpoints that don't exist yet. Board item #1561. Dependency is correctly documented in the issue.
    • paldocs#52 (Embedding pipeline) -- OPEN, in backlog. Required for AC #5 (semantic search via vector results). Dependency is documented in Related but not in Lineage.
    • paldocs#49 (Schema ownership) -- In progress (board item #1559). Must complete before #51 and #52. Indirect dependency.
    • paldocs#55 (Migrate tool references) -- Downstream. Correctly documented as sibling, not dependency.
    • wave:3 label -- Consistent with dependency chain: wave:0 (schema) -> wave:2 (API + embeddings) -> wave:3 (this MCP server) -> wave:4 (migrate references).

    Wave ordering is correct and well-structured.

    Acceptance Criteria

    • [x] AC1 (MCP server initializes) -- verifiable via Claude Code session
    • [x] AC2 (36 tools match old server) -- verifiable but broad. "Results match" is testable by running each tool against both servers.
    • [x] AC3 (no tool name conflicts) -- verifiable. Issue correctly specifies mcp__paldocs__* prefix vs mcp__pal-e-docs__*.
    • [x] AC4 (CRUD operations) -- verifiable via smoke test
    • [ ] AC5 (semantic search) -- DEPENDS on paldocs#52 (embedding pipeline). If #52 is not complete, this AC cannot pass. [BODY] Either add a note that AC5 is deferred until #52 lands, or remove it and create a follow-up AC on #55.

    Blast Radius

    Low blast radius. The new MCP server is purely additive -- it registers as "paldocs" alongside the existing "pal-e-docs" server. No existing tool names are affected. The only risk is tool name collisions, which AC3 explicitly addresses via the mcp__paldocs__* prefix convention.

    No sibling services use a similar MCP pattern that would need the same fix.

    Decomposition Assessment

    • File targets: 1-2 locations (new server code + ~/.mcp.json). Single repo.
    • Acceptance criteria: 5 (at limit but acceptable since AC5 may be deferred)
    • Estimated agent work: The MCP server is described as "~200-300 lines, thin REST proxy". This is a single focused task within the 5-minute rule if using a mature MCP SDK.
    • Verdict: No decomposition needed.

    Recommendation

    • [LABEL] Change arch:infra to arch:mcp (or arch:rails-api). The arch:infra label has no backing note and has been flagged in 4+ previous reviews as too generic. Create arch-mcp note if this is a new architecture component, or use arch:rails-api to match the story note's Related Architecture.
    • [BODY] Clarify AC5 (semantic search): either note it is deferred pending paldocs#52, or move it to paldocs#55 as a post-migration verification criterion.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#50
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- well-formed As/I want/So that
    • [x] Context -- clear motivation and workflow description
    • [x] File Targets -- 6 files to create/modify, 3 exclusions documented
    • [x] Feature Flag -- none (appropriate, new routes with no impact)
    • [x] Acceptance Criteria -- 6 criteria
    • [x] Test Expectations -- 4 tests + run command (bundle exec rspec)
    • [x] Constraints -- dependency, Hotwire requirement, polish scope
    • [x] Checklist -- present
    • [x] Related -- project, story, arch, upstream/downstream noted

    All required sections present. Template is complete.

    Traceability

    • [x] story:sprint-orchestration label -- sprint planning UI for cross-project wave staging and dispatch
    • [x] story note verified -- found in project-paldocs user-stories section (story-paldocs-sprint-orchestration entry)
    • [x] arch:rails-views label -- Rails views component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails-views for the Rails views component
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/56, open

    File Targets

    • [x] app/controllers/sprint_planning_controller.rb -- to be created. No existing controller conflicts. Follows existing pattern (see boards_controller.rb).
    • [x] app/views/sprint_planning/index.html.erb -- to be created. Directory does not exist yet (expected).
    • [x] app/views/sprint_planning/show.html.erb -- to be created. Will need to reference Sprint/SprintProject models from paldocs#50.
    • [x] app/views/sprint_planning/_wave.html.erb -- to be created. Wave partial for grouped items.
    • [x] config/routes.rb -- verified exists. Currently has resources for projects, notes, boards, board_items. Sprint routes will be added.
    • [x] app/views/layouts/ -- verified exists. application.html.erb has a navbar with a brand link. Nav link will be added here.

    All file targets are valid. New files have no conflicts. Existing files are correctly identified.

    Repo Placement

    OK. Issue is filed on ldraney/paldocs, all file targets are in paldocs. Single-repo scope. Correct.

    Dependencies

    • paldocs#50 (Sprint schema -- migrations + models for wave planning) -- board item #1560, currently in todo column, issue is OPEN. This is a hard dependency: Sprint, SprintProject models and board_items.sprint_id/wave_number columns do not exist yet. The ticket correctly documents this dependency in both Lineage and Constraints sections.
    • No other board dependencies found. The ticket is not blocking other items.
    • Board item #1559 (Schema ownership transfer, paldocs#49) is in in_progress -- this is upstream of #50, so transitively upstream of this ticket as well.

    Acceptance Criteria

    6 criteria, all follow the When/Then pattern. All are agent-verifiable:

    • Navigation link -- verifiable via system test or view inspection
    • Sprint creation with project selection -- verifiable via controller test
    • Board items grouped by column -- verifiable via controller test
    • Side-by-side multi-project view -- verifiable via system test
    • Wave staging with sprint_id/wave_number -- verifiable via controller test (depends on schema from #50)
    • Wave display with grouped items and points -- verifiable via view rendering

    Test expectations match: 3 controller tests + 1 system test + run command. Run command (bundle exec rspec) is correct -- repo uses RSpec (spec/ directory confirmed with existing request specs).

    Blast Radius

    Low risk. New controller and views with new routes -- no modification to existing controllers or views. The only existing file modified is config/routes.rb (adding routes) and the layout (adding a nav link). No existing tests or functionality affected.

    The kanban_controller.js Stimulus controller and boards views use drag-and-drop patterns that this ticket's wave staging may want to reuse, but the issue correctly says "keep it functional first, polish later" -- no blast radius concern.

    Decomposition Assessment

    6 file targets in 1 repo. 6 acceptance criteria (slightly exceeds the 5-AC guideline). However, all files are tightly coupled -- one controller with its views and a route entry. Decomposing would create artificial boundaries. Estimated agent work: ~3-4 minutes. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails-views in pal-e-docs for the Rails views component. This note is referenced by multiple board items (this ticket and others with the arch:rails-views label) but does not exist.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#49, part of consolidation
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- MCP layer needs Rails API parity
    • [x] Context -- 36 MCP tools across 7 controller groups detailed
    • [x] File Targets -- 10 files listed with do/don't-touch guidance
    • [x] Feature Flag -- none (correct, new namespace)
    • [x] Acceptance Criteria -- 8 criteria
    • [x] Test Expectations -- 4 expectations with run command
    • [x] Constraints -- JSON shape parity, tsvector search, stub semantic, namespace
    • [x] Checklist -- 8 items
    • [x] Related -- project, story, arch, upstream/downstream/parallel all documented

    All template sections present and complete.

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation note exists in pal-e-docs (user-story type, project paldocs)
    • [x] story note verified -- found in project-paldocs user-stories section ("story-paldocs-consolidation -- absorb pal-e-docs into Rails monolith")
    • [x] arch:rails-api label -- arch-rails-api note exists in pal-e-docs (architecture type, project paldocs)
    • [x] arch note verified -- arch-rails-api note exists with diagram, components table, and key decisions
    • [x] Forgejo issue -- ldraney/paldocs#51, state: open

    Full traceability triangle verified.

    File Targets

    • [x] app/controllers/api/notes_controller.rb -- NEW file. Parent dir app/controllers/ exists. api/ subdir must be created. Note model exists at app/models/note.rb.
    • [x] app/controllers/api/blocks_controller.rb -- NEW file. Block model exists at app/models/block.rb.
    • [x] app/controllers/api/boards_controller.rb -- NEW file. Board is modeled as Note with note_type: "board", not a separate model. Existing UI BoardsController uses Note.find_by!(slug: params[:slug], note_type: "board").
    • [x] app/controllers/api/board_items_controller.rb -- NEW file. BoardItem model exists at app/models/board_item.rb.
    • [x] app/controllers/api/projects_controller.rb -- NEW file. Project model exists at app/models/project.rb.
    • [x] app/controllers/api/repos_controller.rb -- NEW file. Repo model exists at app/models/repo.rb.
    • [x] app/controllers/api/tags_controller.rb -- NEW file. Tag model exists at app/models/tag.rb.
    • [x] app/controllers/api/search_controller.rb -- NEW file. Keyword search uses tsvector (in DB). Semantic search is stub.
    • [x] config/routes.rb -- EXISTS. Currently has only UI routes. API namespace block must be added.
    • [ ] app/views/api/ -- ISSUE: Issue says "jbuilder templates for JSON responses" but jbuilder gem is NOT in the Gemfile or Gemfile.lock. Either add jbuilder or use render json: directly. [BODY] Clarify JSON rendering strategy: add jbuilder gem or use render json: with as_json overrides.

    Repo Placement

    OK. Issue is filed on ldraney/paldocs, all file targets are in the paldocs repo. Single-repo change.

    Dependencies

    • Upstream: paldocs#49 (schema ownership transfer) -- state: open, currently in_progress on board. This ticket depends on #49 completing first (models must exist with correct schema). Models already exist in app/models/ but schema ownership is still transferring.
    • Parallel: paldocs#50 (sprint schema), paldocs#52 (embedding pipeline), paldocs#56 (sprint UI) -- no blocking dependency, can run in parallel.
    • Downstream: paldocs#54 (new MCP server) -- depends on this ticket's API being available.

    Dependencies are well documented in the issue.

    Acceptance Criteria

    8 acceptance criteria -- all are testable via HTTP request specs. Test expectations include request specs per controller, CRUD integration tests, and response shape comparison against pal-e-docs. Run command (bundle exec rspec) is valid.

    However, the "response shape comparison" AC ("When I compare Rails API responses to pal-e-docs responses, then the JSON shapes match") is vague. No reference JSON fixtures or pal-e-docs response examples are provided. The agent will need to call pal-e-docs API or reference the FastAPI source to know the exact shapes.

    Blast Radius

    Low risk. New /api/ namespace is isolated from existing UI routes. No existing controllers are modified. The constraint "Must not break existing paldocs UI routes" is well addressed by the namespace separation. No other repos are affected.

    Decomposition Assessment

    NEEDS DECOMPOSITION

    • File targets: 10 files (8 new controllers + routes + views/serialization), all in 1 repo -- exceeds 3 file target guideline
    • Acceptance criteria: 8 AC -- exceeds 5 AC guideline
    • Scope: 36 MCP tool endpoints across 8 controller groups with JSON shape parity requirements
    • Estimated agent time: Well over 5 minutes -- each controller needs endpoint implementation, JSON serialization, and request specs
    • Recommendation: Decompose into per-controller-group sub-tickets (e.g., notes controller, boards+board_items controller, projects+repos+tags controller, search controller). Each sub-ticket would be 2-3 points with 2-3 file targets.

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

    Recommendation

    • [BODY] Clarify JSON rendering strategy: issue references "jbuilder templates" but jbuilder is not in the Gemfile. Either add gem "jbuilder" to Gemfile or change to render json: approach.
    • [DECOMPOSE] 8 AC across 10 files with 36 endpoint implementations. Route to skill-decompose-ticket. Suggested split: (1) Notes controller (12 tools, 3pts), (2) Boards + Board Items controllers (11 tools, 3pts), (3) Projects + Repos + Tags controllers (7 tools, 2pts), (4) Search controller (2 tools + routes namespace, 2pts).
  • Verdict: READY

    Round 3 re-review after decomposition. All three round 2 findings are resolved: read-only contradiction removed, arch:rails-db label added, ticket decomposed into schema-only (#50) and UI (#56). Scope is tight and fits within the 5-minute rule.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#49 (schema ownership transfer)
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- well-formed As/I want/So that
    • [x] Context -- clear explanation: schema half of sprint planner dogfood, UI comes in #56
    • [x] File Targets -- 7 create/modify targets + 3 do-not-touch entries
    • [x] Feature Flag -- none (correct: new tables and model code, no impact on existing pages)
    • [x] Acceptance Criteria -- 5 criteria, all model-level
    • [x] Test Expectations -- 4 test entries + run command (bundle exec rspec)
    • [x] Constraints -- dependency documented, status values defined, nullable fields specified, unique index specified
    • [x] Checklist -- 7 items
    • [x] Related -- project, story, arch notes, upstream/downstream

    All required sections for a Feature issue are present. Template is complete.

    Traceability

    • [x] story:sprint-orchestration label -- present on board item #1560
    • [x] story note verified -- story-paldocs-sprint-orchestration (id 2093) exists in pal-e-docs, project paldocs
    • [x] story listed on project page -- found in project-paldocs user-stories section
    • [x] arch:rails-db label -- present on board item #1560 (added since round 2)
    • [x] arch note verified -- arch-rails-db (id 1840) exists in pal-e-docs, project paldocs
    • [x] Forgejo issue -- ldraney/paldocs#50, state: open

    Traceability triangle is complete. All backing notes exist and are linked from the project page.

    File Targets

    • [x] db/migrate/XXXXXX_create_sprints.rb -- new file. db/migrate/ dir exists with 2 existing migrations (20260606, 20260613)
    • [x] db/migrate/XXXXXX_create_sprint_projects.rb -- new file, same dir
    • [x] db/migrate/XXXXXX_add_sprint_fields_to_board_items.rb -- new file, same dir
    • [x] app/models/sprint.rb -- new file. app/models/ dir exists with 7 existing models
    • [x] app/models/sprint_project.rb -- new file, same dir
    • [x] app/models/board_item.rb -- exists. Currently has COLUMNS constant, belongs_to :board, and column validation. Issue correctly describes adding sprint/wave associations and scopes
    • [x] app/models/project.rb -- exists. Currently has has_many :notes, :repos and belongs_to :page_note. Issue correctly describes adding has_many :sprint_projects, :sprints through
    • [x] db/structure.sql do-not-touch -- correct, file does not exist yet (awaits paldocs#49)
    • [x] app/controllers/ do-not-touch -- correct, UI is paldocs#56
    • [x] app/views/ do-not-touch -- correct, UI is paldocs#56

    All file targets verified. Existing files match what the issue describes. New files target valid parent directories. The do-not-touch boundaries correctly exclude controller/view/route work (split to #56).

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are within paldocs repo. Single-repo change.

    Dependencies

    • paldocs#49 (schema ownership transfer) -- explicitly documented as upstream dependency. Board item #1559 is in todo column. This ticket cannot start until #49 merges (needs structure.sql and migration infrastructure). Correctly documented in both Lineage and Constraints.
    • paldocs#56 (sprint planning UI) -- explicitly documented as downstream. Board item #1566 is in backlog. #56 depends on this ticket for models and schema. Correctly documented.
    • No circular or undocumented dependencies found.

    Acceptance Criteria

    5 acceptance criteria, all in When/Then format. Assessment:

    • [x] AC 1: Sprint.create with correct defaults -- model-testable via unit spec
    • [x] AC 2: sprint.projects returns selected projects -- model-testable via association spec
    • [x] AC 3: BoardItem.where(sprint:, wave_number:) scoping -- model-testable via scope spec
    • [x] AC 4: sprint.board_items returns tagged items -- model-testable via association spec
    • [x] AC 5: Sprint status scopes (.planning, .active, .complete) -- model-testable via scope spec
    • All criteria are purely data-layer and verifiable with rspec-rails (confirmed in Gemfile). No UI criteria -- correctly split to #56.
    • No contradictions found (round 2 read-only constraint removed).

    Blast Radius

    • BoardItem model changes -- adding sprint_id (FK) and wave_number columns. Both nullable, so existing BoardItem usage in boards_controller.rb, board_items_controller.rb, and views is unaffected. No existing queries filter on these columns.
    • Project model changes -- adding has_many :sprint_projects. Existing associations (notes, repos, page_note) are unaffected.
    • No controller/view/route changes -- schema-only ticket, blast radius is minimal.
    • No similar patterns in sibling services affected.

    Decomposition Assessment

    No decomposition needed. Post-decomposition scope is within bounds:

    • File count: 7 file targets (3 new migrations + 2 new models + 2 model modifications)
    • AC count: 5 acceptance criteria
    • Test count: 4 test expectations
    • Estimated agent work: Under 5 minutes -- migrations and model code are formulaic Rails patterns
    • All three axes are within the 5-minute rule thresholds.

    Round 2 Issues Resolution

    • [x] [BODY] Read-only v1 contradiction -- RESOLVED. Constraint removed. All AC are write operations (create, set, query). No contradiction.
    • [x] [LABEL] arch:rails-db label -- RESOLVED. Board item #1560 now has labels: type:feature,story:sprint-orchestration,arch:rails-db.
    • [x] [DECOMPOSE] Split schema from UI -- RESOLVED. This ticket (#50) is schema+models only. UI split to #56 (board item #1566).

    Recommendation

    No action needed. Ticket is ready for implementation.

  • Verdict: NEEDS_REFINEMENT

    Round 2 re-review. Traceability gaps from round 1 are resolved -- story and arch notes now exist. Two new issues found: scope contradiction and decomposition needed.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#49
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- well-formed As/I want/So that
    • [x] Context -- clear dogfood rationale and sprint workflow description
    • [x] File Targets -- 11 create/modify targets + 2 do-not-touch entries
    • [x] Feature Flag -- none (correct, new routes/tables only)
    • [x] Acceptance Criteria -- 8 criteria
    • [x] Test Expectations -- 5 test entries + run command (bundle exec rspec)
    • [x] Constraints -- dependency, Hotwire, sprint statuses, nullable fields, v1 scope
    • [x] Checklist -- 10 items
    • [x] Related -- project, story, arch notes, upstream/downstream

    All required sections for a Feature issue are present. Template is complete.

    Traceability

    • [x] story:sprint-orchestration label -- present on board item
    • [x] story note verified -- story-paldocs-sprint-orchestration (id 2093) exists in pal-e-docs, linked from project-paldocs user-stories section
    • [x] arch:rails-views label -- present on board item
    • [x] arch note verified -- arch-rails-views (id 1839) exists in pal-e-docs, project paldocs
    • [x] arch-rails-db referenced in issue Related section -- arch-rails-db (id 1840) exists in pal-e-docs, project paldocs
    • [x] Forgejo issue -- ldraney/paldocs#50, state: open

    Traceability triangle is complete. Both arch notes and story note exist. Previous round-1 gaps are resolved.

    File Targets

    • [x] db/migrate/XXXXXX_create_sprints.rb -- new file, db/migrate/ dir exists with 2 existing migrations
    • [x] db/migrate/XXXXXX_create_sprint_projects.rb -- new file, same dir
    • [x] db/migrate/XXXXXX_add_sprint_fields_to_board_items.rb -- new file, same dir
    • [x] app/models/sprint.rb -- new file, models dir exists
    • [x] app/models/sprint_project.rb -- new file, models dir exists
    • [x] app/models/board_item.rb -- exists, currently has basic COLUMNS constant and board association
    • [x] app/models/project.rb -- exists, currently has notes/repos associations
    • [x] app/controllers/sprint_planning_controller.rb -- new file, controllers dir exists
    • [x] app/views/sprint_planning/ -- new directory, views dir exists
    • [x] config/routes.rb -- exists, currently has resources for projects/notes/boards/board_items
    • [x] app/views/layouts/ -- exists, application.html.erb has navbar with navbar-brand link
    • [x] db/structure.sql do-not-touch -- correct, file doesn't exist yet (awaits paldocs#49)
    • [x] app/controllers/projects_controller.rb do-not-touch -- correct, exists and should remain unchanged

    All file targets verified. Existing files match what the issue describes. New files target valid parent directories.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are within paldocs repo. Single-repo change.

    Dependencies

    • paldocs#49 (schema ownership transfer) -- explicitly documented as upstream dependency. Board item #1559 is in todo column. This ticket cannot start until #49 merges (needs structure.sql and migration infrastructure). Correctly documented in both Lineage and Constraints.
    • No other board items appear blocked by or blocking this ticket.

    Acceptance Criteria

    8 acceptance criteria, all in When/Then format. Assessment:

    • [x] AC 1-6 are UI-testable via system specs
    • [x] AC 7-8 are model-testable via unit specs
    • [x] Test command is correct: bundle exec rspec (rspec-rails confirmed in Gemfile)
    • [ ] ISSUE: v1 scope contradiction. AC 4 says "When I stage items into a wave, then they get sprint_id and wave_number set" (write action). But Constraints section says "Keep it functional first, polish later -- read-only wave view for v1." These are contradictory. Staging IS the core feature; recommend removing the "read-only" constraint.

    Blast Radius

    • BoardItem model changes -- adding sprint_id and wave_number columns. Existing BoardItem usage in boards_controller.rb, board_items_controller.rb, and views all reference column/position/board_note_id. New nullable columns should not break existing queries.
    • Project model changes -- adding has_many :sprint_projects. Existing associations (notes, repos, page_note) are unaffected.
    • Navigation addition -- adding a link to application.html.erb navbar. Low risk, additive only.
    • Routes -- additive, no conflicts with existing resource routes.
    • No similar patterns in sibling services affected.

    Decomposition Assessment

    NEEDS DECOMPOSITION.

    • File count: 11 file targets (3 migrations + 3 new models + 2 model modifications + 1 new controller + 1 new view directory with 3+ templates + route changes + layout changes)
    • AC count: 8 acceptance criteria
    • Test count: 5 test expectations including a system test
    • Estimated agent work: Well over 5 minutes -- migrations, models, controller, multiple views, and tests
    • This exceeds the 5-minute rule on all three axes: more than 3 file targets, more than 5 AC, estimated more than 5 minutes of agent work.
    • Recommended split: (a) Sprint schema migrations + models (3 migrations, 3 new models, 2 model updates, model tests) and (b) Sprint planning UI (controller, views, routes, navigation, controller/system tests)

    Recommendation

    • [BODY] Resolve v1 scope contradiction: AC 4 says "stage items into a wave" (write action) but Constraints say "read-only wave view for v1." Either remove the staging AC or remove the "read-only" constraint. Recommend keeping staging (it is the core feature) and removing the "read-only" constraint.
    • [LABEL] Add arch:rails-db label to board item -- issue references arch-rails-db in Related section but the board item label only has arch:rails-views. Sprint schema is a significant DB component.
    • [DECOMPOSE] 11 file targets, 8 AC, estimated more than 5 min agent work. Route to skill-decompose-ticket. Suggested split: (a) sprint schema + models, (b) sprint planning UI + controller + views.
  • Verdict: APPROVED

    Re-review of board item #1559. All 4 issues from the previous review (review-1559-2026-06-22) have been resolved.

    Previous Issues -- Resolution

    • [x] Test command fixed: bundle exec rspec (was rails test)
    • [x] config/application.rb comment: now called out in File Targets ("update ownership comment (lines 42-44)") and in Context section
    • [x] story:consolidation note: story-paldocs-consolidation exists (id 2092), listed on project-paldocs user-stories section
    • [x] arch-rails-db note: arch-rails-db exists (id 1840), full architecture template with ER diagram, components table, key decisions

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- standalone, supersedes pal-e-api#282
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present and clear
    • [x] Context -- good background on ownership transfer rationale, includes application.rb comment update note
    • [x] File Targets -- 5 modify/create targets, 2 exclusions, application.rb comment explicitly targeted
    • [x] Feature Flag -- none, justified (default status preserves behavior)
    • [x] Acceptance Criteria -- 6 items
    • [x] Test Expectations -- correct: bundle exec rspec
    • [x] Constraints -- 4 items, well-scoped
    • [x] Checklist -- present, includes application.rb comment update
    • [x] Related -- present, references story and arch notes

    Traceability

    • [x] story:consolidation label -- story-paldocs-consolidation note exists, listed on project-paldocs user-stories section
    • [x] story note verified -- found in project-paldocs user-stories section (4th entry)
    • [x] arch:rails-db label -- arch-rails-db note exists with ER diagram and component table
    • [x] arch note verified -- arch-rails-db note (id 1840) exists in pal-e-docs, tags: architecture, active
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/49, open

    File Targets

    • [x] db/structure.sql -- does not exist yet (correct, will be created via pg_dump import)
    • [x] config/application.rb -- verified: exists, line 46 currently config.active_record.schema_format = :ruby, needs change to :sql. Lines 42-44 contain the ownership comment that must be updated. Both called out in issue.
    • [x] Gemfile -- verified: exists, no neighbor gem present currently
    • [x] db/migrate/XXXXXX_add_status_to_projects.rb -- will be created (correct, 2 existing migrations in db/migrate/)
    • [x] app/models/project.rb -- verified: exists, no status scopes currently (8 lines, basic model with has_many/belongs_to)

    Repo Placement

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

    Dependencies

    • No blocking dependencies on the board. This is wave:0 -- it is the foundation that wave:1 (#1560 sprint planning UI), wave:2 (#1561 API endpoints, #1562 embedding pipeline), wave:3 (#1564 MCP server), and wave:4 (#1565 migration) depend on.
    • Downstream wave ordering correctly captured in issue Related section.
    • Requires access to production database for pg_dump. Constraint documented in issue.

    Acceptance Criteria

    • [x] AC 1-5 are verifiable by an agent (run commands, check output)
    • [x] AC 6 ("When I check pal-e-docs Alembic, then no new migrations are being generated there") -- coordination concern, acceptable as manual check
    • [x] Test command is bundle exec rspec -- matches project test framework (rspec-rails in Gemfile, spec/ directory, .rspec config)

    Blast Radius

    • pal-e-docs (FastAPI) -- continues to read/write the same database. Adding status VARCHAR(50) DEFAULT 'active' column is additive and non-breaking.
    • pal-e-mcp -- proxies to pal-e-docs API, unaffected by schema additions.
    • Embedding worker -- writes to blocks.embedding column, unaffected by projects table changes.
    • production.rb -- also sets dump_schema_after_migration = false (line 57). Agent should be aware of this but the issue's constraint scope handles it.
    • schema.rb -- does not exist currently (dump disabled). Switching to :sql format is clean.

    Decomposition Assessment

    5 file targets, 1 repo, 6 acceptance criteria. Estimated agent work under 5 minutes. No decomposition needed.

    Recommendation

    No action needed. All previous NEEDS_REFINEMENT issues have been resolved. Ticket is ready for implementation.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#49
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present, well-formed
    • [x] Context -- good background on MCP proxy pattern
    • [x] File Targets -- present (app/controllers/api/, config/routes.rb, app/serializers/)
    • [x] Feature Flag -- none, justified (new namespace)
    • [x] Acceptance Criteria -- 7 criteria present
    • [x] Test Expectations -- request tests + integration tests specified
    • [x] Constraints -- JSON shape matching, tsvector, semantic stub
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [ ] story:consolidation label -- story NOT listed in project-paldocs user-stories section. Only kanban, project-pages, and auth stories exist. [SCOPE] Create user story entry on project-paldocs user-stories section for "consolidation" (pal-e-docs into Rails monolith).
    • [ ] arch:rails-api label -- no arch note arch-rails-api found in pal-e-docs. [SCOPE] Create architecture note arch-rails-api for the Rails API layer.
    • [x] Forgejo issue -- ldraney/paldocs#51, open

    File Targets

    • [x] app/controllers/api/ -- does not exist yet, will be created. Correct for new API namespace.
    • [x] config/routes.rb -- exists. Currently has UI-only routes (projects, notes, boards, board_items). API namespace will be added.
    • [x] app/serializers/ -- does not exist yet, will be created. No jbuilder or serializer gem in Gemfile currently; ticket should specify which approach (jbuilder vs active_model_serializers vs plain as_json).
    • [x] app/models/ -- correctly marked as "do not touch." Models exist: note, block, board_item, project, repo, tag, note_tag.

    Repo Placement

    OK -- issue filed on ldraney/paldocs, fix is in ldraney/paldocs. The downstream repoint of pal-e-mcp (changing PALDOCS_BASE_URL and possibly SDK calls) is a separate repo (ldraney/pal-e-mcp). The issue correctly defers that to the downstream "MCP repoint" ticket (paldocs#53).

    Dependencies

    • Upstream: paldocs#49 (schema ownership transfer) -- correctly documented, still open.
    • Parallel: paldocs#50 (sprint UI, wave:1), paldocs#52 (embedding pipeline, wave:2) -- documented.
    • Downstream: paldocs#53 (repoint MCP + deprecate pal-e-docs, wave:3) -- documented.
    • Board items #1559 (schema, wave:0), #1560 (sprint UI, wave:1), #1562 (embeddings, wave:2), #1563 (repoint, wave:3) all present on board-paldocs.

    Acceptance Criteria

    7 AC present. Testable in principle but problematic in practice:

    • AC7 ("compare Rails API responses to pal-e-docs responses, then JSON shapes match") is vague -- which endpoints? All 47? The issue lists only ~6 endpoint groups but the actual pal-e-docs API surface is 47 endpoints across 8 route files.
    • AC cover only notes, boards, projects, and search -- missing repos, links, blocks (toc, section, compiled page, rebalance), board advanced endpoints (sync, sync-issues, activity, backlog).
    • Test command "rails test" conflicts with Gemfile which uses rspec-rails. Should be "bundle exec rspec".

    Blast Radius

    • pal-e-mcp uses PalEDocsClient SDK (pal-e-sdk) which constructs HTTP calls to PALDOCS_BASE_URL. The SDK parses JSON responses and expects exact shapes. Any deviation in Rails JSON output will break all 36 MCP tools.
    • Existing paldocs UI routes (projects, notes, boards, board_items) must not be affected. The /api/ namespace provides proper isolation.
    • The pal-e-docs FastAPI Pydantic response models (NoteOut, BoardItemOut, ProjectOut, etc.) define the contract. Rails serializers must match these exactly.

    Decomposition Assessment

    NEEDS DECOMPOSITION -- fails all three 5-minute-rule checks:

    • File targets: 6+ new controller files + serializers + routes + request specs = 15+ files in 1 repo. Exceeds the >3 file threshold.
    • Acceptance criteria: 7 AC listed, but actual surface is 47 endpoints. Far exceeds the >5 AC threshold.
    • Estimated agent work: 47 endpoints with JSON shape matching, request specs for each -- well beyond 5 minutes. Estimated 30-60 minutes of agent work.

    Recommended decomposition by endpoint group (each fits a single agent pass):

    1. Notes CRUD + search (8 endpoints) -- 8pts
    2. Boards + Board Items (14 endpoints) -- 8pts
    3. Blocks (toc, section, compiled, CRUD, rebalance -- 8 endpoints) -- 5pts
    4. Projects + Repos + Tags + Links (14 endpoints) -- 5pts
    5. Search: semantic stub (1 endpoint) -- 2pts

    Route to skill-decompose-ticket.

    Recommendation

    • [SCOPE] Create user story entry "consolidation" on project-paldocs user-stories section.
    • [SCOPE] Create architecture note arch-rails-api for the Rails API layer.
    • [BODY] Fix test command: "rails test" should be "bundle exec rspec" (project uses rspec-rails).
    • [BODY] Issue claims "14 MCP tools" but actual surface is 36 MCP tools mapping to 47 HTTP endpoints. Update Context section with accurate endpoint count. Add missing groups: repos CRUD (5 endpoints), links (2 endpoints), blocks/toc/section/compiled/rebalance (8 endpoints), board advanced (activity, backlog, sync-issues).
    • [BODY] Specify JSON serialization approach -- jbuilder, active_model_serializers, or plain as_json. No serializer gem currently in Gemfile.
    • [DECOMPOSE] 47 endpoints across 6+ controllers with full JSON shape matching and request specs. Far exceeds 5-minute rule. Route to skill-decompose-ticket with 5-sub-ticket split (notes, boards, blocks, projects+repos+tags+links, search).
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#51, paldocs#52
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- well-formed As/I want/So that
    • [x] Context -- clear motivation
    • [x] File Targets -- present (but incomplete, see below)
    • [x] Feature Flag -- "none" with rollback rationale
    • [x] Acceptance Criteria -- 5 criteria present
    • [x] Test Expectations -- manual verification described
    • [x] Constraints -- rollback plan, human gate for archive
    • [x] Checklist -- present
    • [x] Related -- upstream deps and affected repos listed

    Traceability

    • [x] story:consolidation label -- present on board item
    • [ ] story note MISSING -- project-paldocs user-stories section lists only: kanban, project-pages, auth. No "consolidation" story entry. [SCOPE] Create user story entry "story-paldocs-consolidation" on project-paldocs user-stories section.
    • [x] arch:infra label -- present on board item
    • [ ] arch note MISSING -- search for "arch-infra" returned zero results in pal-e-docs. [SCOPE] Create architecture note arch-infra for the infra component.
    • [x] Forgejo issue -- paldocs#53, state: open

    File Targets

    • [x] ~/.mcp.json -- verified: file exists, contains PALDOCS_BASE_URL pointing to https://pal-e-docs.tail5b443a.ts.net
    • [x] ~/claude-custom/docs/mcp-servers.md -- verified: file exists (7.8k)
    • [x] ~/claude-custom/docs/operations.md -- verified: file exists (5.0k)
    • [ ] MISSING: 29 additional files in ~/claude-custom/ reference "pal-e-docs" -- agents (dev.md, qa.md, overseer.md), skills (8 SKILL.md files), hooks (15 scripts), settings.json, settings.local.json, plus 6 more docs files (agents.md, enforcement.md, settings.md, skills.md, hooks.md, filetree.md). Issue only lists 2 doc files as targets.
    • [ ] MISSING: ~/pal-e-mcp/ -- server.py, pyproject.toml, README.md, and all tool module docstrings reference "pal-e-docs". Issue says "do not touch" but docstrings and README will be stale after cutover.
    • [ ] MISSING: ~/pal-e-services/terraform/cnpg.tf -- has pal-e-docs DB credential secret comment
    • [x] ~/pal-e-mcp/ listed as "do not touch" for code -- correct, only env var changes
    • [x] ~/pal-e-docs/ listed as "do not touch" -- correct, archived not modified

    Repo Placement

    Issue filed on ldraney/paldocs but actual changes span 4 repos:

    • ~/.mcp.json -- local config, not repo-tracked (OK)
    • ~/claude-custom/ -- separate repo (ldraney/claude-custom), 29+ files affected
    • ~/pal-e-services/ -- separate repo (k8s teardown via tofu, cnpg.tf cleanup)
    • ~/pal-e-mcp/ -- separate repo (README/docstring updates for accuracy)

    Multi-repo scope is acknowledged in the issue ("Affects: pal-e-mcp, claude-custom docs") but the file targets section severely understates the work in claude-custom. With 29+ files across 4 repos, this needs per-repo sub-tickets.

    Dependencies

    • paldocs#51 (API endpoints, board item #1561) -- backlog, open. HARD blocker. Rails must serve the full REST surface before MCP can repoint.
    • paldocs#52 (Embedding pipeline, board item #1562) -- backlog, open. HARD blocker. Semantic search must work in Rails before cutover.
    • Both upstream dependencies are wave:2; this ticket is wave:3. Ordering is correct.
    • This ticket blocks nothing on the board currently, but it effectively gates the deprecation of the pal-e-docs project.

    Acceptance Criteria

    • [x] AC1: "14 MCP tools return correct results from Rails" -- testable via manual MCP calls
    • [x] AC2: "semantic_search matches previous FastAPI behavior" -- testable
    • [x] AC3: "pal-e-docs k8s namespace has no pods" -- testable via kubectl
    • [x] AC4: "pal-e-docs Forgejo repo is archived" -- testable via Forgejo API
    • [ ] AC5: "claude-custom docs reflect Rails monolith" -- too vague. Should specify all 8 docs files, CLAUDE.md/README.md, plus 21 agent/skill/hook files that reference pal-e-docs.
    • [ ] MISSING AC: "No hooks or skills reference stale pal-e-docs URLs or names after update" -- 29 files need audit
    • [ ] MISSING AC: "pal-e-mcp README and docstrings reflect new architecture"

    Blast Radius

    Critical finding: The ticket lists 3 file targets but the actual blast radius is far larger:

    • claude-custom docs/: 8 files, 38 references to "pal-e-docs"
    • claude-custom hooks/: 15 hook scripts (block-docs-writes.sh alone has 23 references)
    • claude-custom agents/: 3 agent definitions (overseer.md: 11 refs, qa.md: 6, dev.md: 2)
    • claude-custom skills/: 8 skill files
    • claude-custom settings: settings.json (4 refs), settings.local.json (1 ref)
    • pal-e-mcp/: server.py, pyproject.toml, README.md, 7 tool modules with docstrings
    • pal-e-services/: terraform/cnpg.tf (DB credentials secret)

    Key scoping question: many references use "pal-e-docs" as the MCP server name (e.g., mcp__pal-e-docs__get_note in settings.json). The ticket must decide: rename the MCP server from "pal-e-docs" to "paldocs" everywhere (breaking change for all tool references), or keep the legacy name with a new backend URL? This decision drives whether the blast radius is 3 files (URL swap only) or 50+ files (full rename).

    Decomposition Assessment

    NEEDS DECOMPOSITION -- fails the 5-minute rule on all three dimensions:

    • File count: 30+ files across 4 repos -- far exceeds the >3 files / >2 repos threshold
    • AC count: 5 stated + 2 missing = 7 -- exceeds >5 threshold
    • Estimated agent time: well beyond 5 minutes for a single pass

    Suggested sub-ticket decomposition:

    1. Repoint PALDOCS_BASE_URL and verify 14 MCP tools (~/.mcp.json swap + manual verification)
    2. Update claude-custom docs (8 files, 38 references)
    3. Update claude-custom hooks/agents/skills/settings (21 files, rename decision required)
    4. Remove pal-e-docs k8s deployment (pal-e-services tofu)
    5. Archive pal-e-docs Forgejo repo (human-gated)
    6. Update pal-e-mcp README and docstrings

    Recommendation

    • [SCOPE] Create user story note story-paldocs-consolidation and add entry to project-paldocs user-stories section.
    • [SCOPE] Create architecture note arch-infra for the infra component.
    • [BODY] Add missing file targets: the 29 files in claude-custom (hooks, agents, skills, settings) plus pal-e-mcp README/docstrings plus pal-e-services terraform.
    • [BODY] Add missing AC: "No hooks or skills reference pal-e-docs incorrectly after update" and "pal-e-mcp README reflects new architecture."
    • [BODY] Add scoping decision to Context section: rename MCP server from "pal-e-docs" to "paldocs" everywhere, or keep legacy name with new URL?
    • [DECOMPOSE] 30+ files across 4 repos, 7 AC -- route to skill-decompose-ticket. Suggested 6 sub-tickets above.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- standalone, supersedes pal-e-api#282
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present and clear
    • [x] Context -- good background on ownership transfer rationale
    • [x] File Targets -- 5 modify/create targets, 2 exclusions
    • [x] Feature Flag -- none, justified (default status preserves behavior)
    • [x] Acceptance Criteria -- 6 items
    • [ ] Test Expectations -- ISSUE: says rails test but project uses RSpec (rspec-rails in Gemfile, spec/ directory). Should be bundle exec rspec
    • [x] Constraints -- 4 items, well-scoped
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [ ] story:consolidation label -- story note MISSING. project-paldocs user-stories section lists: kanban, project-pages, auth. No consolidation story. [SCOPE] Create user story entry on project-paldocs user-stories section for story:consolidation.
    • [ ] arch:rails-db label -- arch note MISSING. No arch-rails-db note found in pal-e-docs. [SCOPE] Create architecture note arch-rails-db for the Rails database layer component.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/49, open

    File Targets

    • [x] db/structure.sql -- does not exist yet (correct, will be created via pg_dump import)
    • [x] config/application.rb -- verified: exists, line 46 currently config.active_record.schema_format = :ruby, needs change to :sql
    • [x] Gemfile -- verified: exists, no neighbor gem present currently
    • [x] db/migrate/XXXXXX_add_status_to_projects.rb -- will be created (correct, 2 existing migrations in db/migrate/)
    • [x] app/models/project.rb -- verified: exists, no status scopes currently (8 lines, basic model with has_many/belongs_to)

    Repo Placement

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

    Dependencies

    • No blocking dependencies on the board. This is wave:0 -- it is the foundation that wave:1 (sprint planning UI, #1560) and wave:2 (API endpoints #1561, embedding pipeline #1562) depend on.
    • Downstream items #1560, #1561, #1562, #1563 all depend on this completing first. Wave ordering correctly captures this.
    • Requires access to production database for pg_dump. Constraint documented in issue.

    Acceptance Criteria

    • [x] AC 1-5 are verifiable by an agent (run commands, check output)
    • [ ] AC 6 ("When I check pal-e-docs Alembic, then no new migrations are being generated there") -- not directly agent-verifiable from the paldocs repo. This is a coordination concern, not a code change. Acceptable as a manual check.
    • [ ] Test Expectations say rails test but should say bundle exec rspec -- project uses RSpec, not minitest

    Blast Radius

    • pal-e-docs (FastAPI) -- continues to read/write the same database. Adding a status VARCHAR(50) DEFAULT 'active' column is additive and non-breaking. Existing rows get default value.
    • pal-e-mcp -- proxies to pal-e-docs API, unaffected by schema additions.
    • Embedding worker -- writes to blocks.embedding column, unaffected by projects table changes.
    • application.rb comment -- lines 42-44 say "Tables are managed by pal-e-docs FastAPI -- Rails migrations are limited to additive objects (triggers, indexes)." This comment must be updated since the ticket changes this paradigm. Not mentioned in file targets.
    • schema.rb -- does not exist currently (dump disabled). Switching to :sql format is clean.

    Decomposition Assessment

    5 file targets, 1 repo, 6 acceptance criteria. Estimated agent work under 5 minutes. No decomposition needed.

    Recommendation

    • [BODY] Fix test command: rails test should be bundle exec rspec
    • [BODY] Add file target note: update comment in config/application.rb lines 42-44 to reflect Rails as schema owner
    • [SCOPE] Create user story entry story-paldocs-consolidation on project-paldocs user-stories section
    • [SCOPE] Create architecture note arch-rails-db for the Rails database layer component
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- depends on paldocs#49, supersedes paldocs#48
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- As Lucas, sprint planning interface
    • [x] Context -- dogfood ticket, reads existing board data
    • [x] File Targets -- 4 create/modify targets, 2 do-not-touch
    • [x] Feature Flag -- none (justified: new route, no impact)
    • [x] Acceptance Criteria -- 6 criteria
    • [x] Test Expectations -- 3 test items + run command
    • [x] Constraints -- 4 constraints listed
    • [x] Checklist -- 7 items
    • [x] Related -- project-paldocs, upstream ref, story label

    Traceability

    • [x] story:sprint-orchestration label present
    • [ ] story note MISSING -- [SCOPE] The project-paldocs user-stories section lists kanban, project-pages, and auth stories only. No sprint-orchestration story entry exists. Create user story entry on project-paldocs.
    • [x] arch:rails-views label present
    • [ ] arch note MISSING -- [SCOPE] arch-rails-views is referenced on project-paldocs architecture section but the actual pal-e-docs note does not exist (search returned empty). Create architecture note arch-rails-views.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/50, open

    File Targets

    • [x] app/controllers/sprint_planning_controller.rb -- to be created. No existing sprint planning controller. Correct.
    • [x] app/views/sprint_planning/ -- to be created. No existing directory. Correct.
    • [x] config/routes.rb -- exists. Currently has resources for projects, notes, boards, board_items. Sprint planning route will be added here. Correct.
    • [x] app/views/layouts/ -- exists with application.html.erb. Nav link to be added in header navbar. Correct.
    • [x] app/models/project.rb -- do-not-touch is correct: currently has no status scopes (paldocs#49 dependency)
    • [x] db/ -- do-not-touch is correct: no schema changes needed for this ticket

    Repo Placement

    OK. Issue filed on ldraney/paldocs, work targets ldraney/paldocs. Single repo. No mismatch.

    Dependencies

    • paldocs#49 (schema ownership transfer) -- BLOCKING. Issue is open. The projects table currently has no status column. The Project model has no status scopes. The ticket explicitly says "Depends on paldocs#49 merging first (needs status scopes)" and the Context section says it "uses the new projects.status field from paldocs#49 to filter active projects." This dependency is real and verified.
    • Board item #1559 (paldocs#49) is in backlog column on board-paldocs. It has not been reviewed or moved to todo yet.
    • paldocs#48 is superseded (closed).
    • No other board items appear to block or be blocked by this ticket.

    Acceptance Criteria

    6 acceptance criteria. All are testable by an agent:

    • AC1: navigate to sprint planning, see active projects -- verifiable via controller test + system test
    • AC2: select projects, see board items by column -- verifiable via controller test
    • AC3: multiple project boards side-by-side -- verifiable via system test
    • AC4: backlog/todo items show points and labels -- verifiable via view test
    • AC5: identify independent tickets for wave staging -- somewhat vague for v1 read-only; acceptable since Constraints say "read-only for v1"
    • AC6: navigation link from main layout -- verifiable via view inspection

    Test expectations are concrete: controller tests for active projects and board items, system test for navigation flow. Run command is rails test.

    Blast Radius

    Low blast radius. This is a new controller and new views -- no modification to existing controllers, models, or views beyond adding a route and a nav link. The layout change (adding a nav link) is minimal. No downstream consumers affected. No similar patterns to check for bugs.

    Decomposition Assessment

    4 file targets in 1 repo. 6 acceptance criteria (under the 5+ threshold but borderline). Estimated agent work: under 5 minutes for a new controller + 2-3 view templates + route + nav link. No decomposition needed.

    Recommendations

    • [SCOPE] Create user story entry story-paldocs-sprint-orchestration on project-paldocs user-stories section. The story:sprint-orchestration label exists but has no backing note.
    • [SCOPE] Create architecture note arch-rails-views in pal-e-docs. Referenced on project-paldocs but note does not exist.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Standalone, new initiative
    • [x] Repo — listed (primary + 4 ancillary)
    • [x] User Story — present, well-formed
    • [x] Context — thorough, references existing spike and reference implementation
    • [x] File Targets — extensive, organized by phase
    • [x] Feature Flag — "none" with rationale
    • [x] Acceptance Criteria — 9 criteria listed
    • [x] Test Expectations — 4 items with run command
    • [x] Constraints — 5 constraints listed
    • [x] Checklist — present
    • [x] Related — present

    All required feature template sections are present and well-populated.

    Traceability

    • [ ] story:ios-testflight label — story note MISSING. No story-paldocs-ios-testflight note exists in pal-e-docs. The project-paldocs user-stories section lists story-paldocs-auth, story-paldocs-kanban, and story-paldocs-project-pages but not an ios-testflight story. [SCOPE] Create user story note story-paldocs-ios-testflight and add entry to project-paldocs user-stories section. Alternatively, if auth is the primary story, relabel to story:auth and add iOS as a sub-scope.
    • [ ] arch:full-stack label — arch note MISSING. No arch-full-stack note exists in pal-e-docs. The work actually spans arch:ci-cd (DNS/Caddy), arch:keycloak (client config), arch:rails-views (auth controllers), and iOS (new domain). [SCOPE] Either create arch-full-stack as a composite architecture note, or decompose the ticket so each sub-ticket has a specific arch label.
    • [x] Forgejo issue — ldraney/paldocs#46, open

    File Targets

    Phase 1 — DNS (pal-e-platform):

    • [x] terraform/dns.tf — verified: exists at /home/ldraney/pal-e-platform/terraform/dns.tf. Contains GoDaddy A records for palinks.app and landscaping-assistant.app — pattern established.
    • [x] salt/pillar/caddy.sls — verified: exists at /home/ldraney/pal-e-platform/salt/pillar/caddy.sls. No paldocs config yet (0 matches) — confirms new work.

    Phase 2 — Keycloak (pal-e-services):

    • [x] terraform/k3s.tfvars — verified: exists (symlink to secrets). Already contains a paldocs block with forgejo_repo, image_repo, port 3000. Note: this appears to be the FluxCD/deployment config, NOT the Keycloak client config. The issue says "add paldocs confidential client to pal-e-docs realm" — verify this is the correct file for Keycloak client definitions, or if a separate Keycloak terraform file is needed. [BODY] Clarify whether k3s.tfvars is the correct location for Keycloak client config, or if a dedicated Keycloak terraform file exists.

    Phase 3 — Rails auth (paldocs):

    • [x] Gemfile — verified: exists. No omniauth references yet — clean starting point.
    • [x] config/initializers/omniauth.rb — does not exist yet (will be created). Initializers directory exists.
    • [x] app/controllers/sessions_controller.rb — does not exist yet (will be created). Controllers directory exists.
    • [x] app/controllers/application_controller.rb — verified: exists.
    • [x] config/routes.rb — verified: exists.
    • [x] Reference implementation files verified: ~/landscaping-assistant/config/initializers/omniauth.rb and ~/landscaping-assistant/app/controllers/sessions_controller.rb both exist.

    Phase 3b — Deployment (pal-e-deployments):

    • [x] overlays/paldocs/prod/deployment-patch.yaml — verified: exists.

    Phase 4 — iOS (paldocs-ios):

    • [ ] paldocs-ios repo — does NOT exist on Forgejo or locally. Must be created on MacBook. This is expected (issue says "new"), but the repo creation is an implicit prerequisite not captured in the checklist.
    • [x] macbook-ssh/docs/ — verified: exists. Contains dev-build-guide.md with HotwireNative references.

    Related doc:

    • [x] docs/auth-visibility-architecture.md — verified: exists in paldocs repo.

    Repo Placement

    The Forgejo issue is filed on ldraney/paldocs, which is the primary repo. However, the work spans 5 repos: paldocs, pal-e-platform, pal-e-services, pal-e-deployments, and paldocs-ios (new). This is appropriate for the umbrella issue but reinforces the need for decomposition — each sub-ticket should be filed on its target repo.

    Dependencies

    • Board item #1369 ("Deploy paldocs to production") is in next_up column. This is a prerequisite — DNS and public access require production deployment to be complete first. The issue does not document this dependency.
    • Backlog items #1387, #1388, #1389, #1390 are auth-related (story:auth) and may overlap or conflict with this ticket's auth implementation. Relationship is undocumented.
    • Keycloak pal-e-docs realm — issue states it exists. No verification needed but dependency is implicit.
    • GoDaddy domain ownership — issue states paldocs.app is purchased. Implicit dependency.
    • MacBook access — Phase 4 (iOS) requires MacBook for Xcode/TestFlight. Not executable by server-side agents.

    Acceptance Criteria

    9 acceptance criteria are listed. Assessment:

    • AC 1-2 (DNS/TLS): Verifiable via curl -I https://paldocs.app and redirect check. Automatable.
    • AC 3-6 (Auth flow): AC 3-5 partially testable via request specs. AC 6 (logout) requires Keycloak integration. Semi-automatable.
    • AC 7-9 (iOS): Requires physical device and TestFlight. Not automatable by agents — must be manual.

    Missing AC: No criterion for www.paldocs.app CNAME/A record setup (only redirect behavior is specified, not DNS record creation).

    Blast Radius

    • Caddy config: Adding a new site block to caddy.sls affects the reverse proxy for all sites. Pattern is established (palinks.app, landscaping-assistant.app) so risk is low if pattern is followed.
    • Keycloak realm: Adding a client to the existing pal-e-docs realm is additive — no risk to existing clients.
    • paldocs app: Adding auth to application_controller.rb affects ALL routes. Must ensure health check / readiness endpoints remain unauthenticated. The issue does not address this.
    • Existing auth backlog items: Items #1387-1390 (story:auth) in backlog may become partially completed or obsoleted by this ticket. Coordination needed.

    Decomposition Assessment

    NEEDS DECOMPOSITION.

    • 13+ file targets across 5 repos — exceeds threshold of 3 files across 2 repos
    • 9 acceptance criteria — exceeds threshold of 5
    • 4 distinct phases with different toolchains (Terraform, Salt, Ruby, Swift/Xcode)
    • Estimated agent work: well over 5 minutes per phase
    • Phase 4 (iOS) cannot be executed by server-side agents at all

    Recommended decomposition into 4 sub-tickets matching the phases in the issue:

    1. DNS + Caddy (pal-e-platform, ~3 pts) — arch:ci-cd
    2. Keycloak client (pal-e-services, ~2 pts) — arch:keycloak
    3. Rails auth + deployment secrets (paldocs + pal-e-deployments, ~5 pts) — arch:rails-views
    4. iOS turbo-ios + TestFlight (paldocs-ios, ~5 pts) — arch:ios (new), MacBook-only

    Recommendation

    • [SCOPE] Create user story note story-paldocs-ios-testflight (or relabel to story:auth if iOS is considered part of auth story) and add to project-paldocs user-stories section.
    • [SCOPE] Create architecture note arch-full-stack, or decompose so each sub-ticket uses a specific arch label.
    • [BODY] Clarify whether terraform/k3s.tfvars is the correct file for Keycloak client configuration (current content appears to be deployment/FluxCD config, not Keycloak).
    • [BODY] Add dependency note: blocked by #1369 (Deploy paldocs to production) which is in next_up.
    • [BODY] Add AC for health check endpoints remaining unauthenticated after auth is added.
    • [BODY] Add prerequisite to checklist: "paldocs-ios repo created on Forgejo/GitHub".
    • [BODY] Document relationship to existing auth backlog items #1387-1390 (story:auth) — are they superseded, prerequisites, or parallel?
    • [DECOMPOSE] 13+ file targets across 5 repos, 9 AC, 4 phases with different toolchains. Route to skill-decompose-ticket for sub-ticket creation.
  • Verdict: READY

    Re-review of board item #1386. All 4 issues from prior review (review-1386-2026-06-07) have been addressed.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage
    • [x] Repo
    • [x] User Story
    • [x] Context
    • [x] File Targets (including "Files NOT to touch")
    • [x] Feature Flag (none -- appropriate for UI-only change)
    • [x] Acceptance Criteria (7 items)
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    Traceability

    • [x] story:project-pages label -- present on board item
    • [x] story note verified -- story-paldocs-project-pages exists in pal-e-docs (title: "User Story: Project Pages", project: paldocs). Includes accordion-specific story: "As a superadmin, I want sections collapsed by default showing only recent items so I can find what's active"
    • [x] story listed on project page -- found in project-paldocs user-stories section
    • [x] arch:rails-hotwire label -- present on board item
    • [x] arch note verified -- arch-rails-hotwire exists in pal-e-docs (title: "Architecture: Rails Hotwire/Stimulus Patterns", project: paldocs). Documents Stimulus controllers, Turbo Frames, and progressive enhancement pattern (details/summary baseline)
    • [x] arch note listed on project page -- found in project-paldocs architecture section
    • [x] Forgejo issue -- ldraney/paldocs#25, state: open

    File Targets

    • [x] app/views/projects/show.html.erb -- verified: exists, currently renders flat note-group lists (lines 36-54) that will be replaced with accordion components
    • [x] app/controllers/projects_controller.rb -- verified: exists, line 8 currently sorts by title (sort_by(&:title)), needs change to updated_at: :desc
    • [x] app/assets/stylesheets/ -- verified: directory exists with application.css containing existing .note-group styles (line 438+)
    • [x] app/javascript/controllers/ -- verified: directory exists with existing controllers (kanban_controller.js). Importmap configured with pin_all_from so new controllers auto-register

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are in the same repo. Single-repo change.

    Dependencies

    • #1344 (issue #13, Postgres trigger for projects.updated_at) -- currently in_progress on the board. Issue correctly documents this is NOT a blocker: accordion sorting uses notes.updated_at (note-level freshness, already populated) not projects.updated_at (project-level freshness). Independent work streams.
    • #1343 (render project page note content) -- done. The page note rendering in show.html.erb lines 25-34 coexists with the notes-by-type section and is unaffected by accordion changes.
    • No undocumented dependencies found.

    Acceptance Criteria

    All 7 ACs are verifiable by an agent:

    • AC 1-3 (accordion structure, collapsed default, header+count) -- verifiable via integration test checking rendered HTML for <details>/<summary> elements
    • AC 4 (updated_at sort) -- verifiable via integration test checking note order in rendered output
    • AC 5-6 (top 5 + "show all" link) -- verifiable via integration test with fixture data
    • AC 7 (Turbo persistence via data-turbo-permanent) -- implementation hint now specified. No existing system test infrastructure (spec dir has only helper files), so verifiable by checking the attribute is present in rendered HTML

    Test command bundle exec rspec spec/ is valid for the project.

    Blast Radius

    Low. The note-group/note-list pattern only appears in projects/show.html.erb. No other views render notes by type. The projects/index.html.erb lists projects only, not notes. Existing CSS classes (.note-group, .note-list, etc.) will be refactored but are scoped to the project detail page.

    Decomposition Assessment

    4 file targets in 1 repo. 7 acceptance criteria (above the 5 threshold) but all describe facets of a single cohesive accordion feature on one page. Estimated agent work: ~3-4 minutes (controller sort change, view template refactor to details/summary, CSS additions, optional Stimulus controller). No decomposition needed -- this is one coherent unit.

    Prior Review Issues -- Resolution Status

    • [x] Issue 1: story-paldocs-project-pages note now exists with user stories including the accordion story
    • [x] Issue 2: arch-rails-hotwire note created with Stimulus/Turbo patterns and progressive enhancement guidance
    • [x] Issue 3: AC 7 updated with explicit data-turbo-permanent implementation hint
    • [x] Issue 4: Related section clarifies #13 is not a blocker (note-level vs project-level updated_at)

    Recommendations

    No action needed.

  • Verdict: APPROVED

    Re-review of board item #1384. Prior review (review-1384-2026-06-07) found 4 issues; all have been addressed.

    Template Completeness

    • [x] Type -- Spike
    • [x] Lineage -- Standalone, operational need stated
    • [x] Repo -- Multiple repos listed with primary identified (ldraney/paldocs)
    • [x] Question -- Clear top-level question with 8 sub-questions covering client setup, roles, visibility model, enforcement, sessions, public access, agent access, migration
    • [x] Deliverables -- docs/auth-visibility-architecture.md + follow-up tickets
    • [x] Time-box -- 1 session
    • [x] Related -- project-paldocs, sop-keycloak-client-creation, board-paldocs, #22 dependency, arch-rails-app prior art, arch-keycloak-paldocs stub all referenced

    Traceability

    • [x] story:auth label present
    • [x] story note verified -- story-paldocs-auth exists with Superadmin and Member user stories, scoped to Keycloak-based auth and visibility
    • [x] story listed on project page -- project-paldocs user-stories section includes story-paldocs-auth entry
    • [x] arch:keycloak label present
    • [x] arch note verified -- arch-keycloak (platform-wide, pal-enterprises) exists; arch-keycloak-paldocs (project-specific stub) also exists with pending decisions and prior art references
    • [x] arch listed on project page -- project-paldocs architecture section includes arch-keycloak-paldocs entry
    • [x] Forgejo issue -- ldraney/paldocs#23, open

    File Targets

    N/A -- Spike type. Deliverable is a new doc to be created (docs/auth-visibility-architecture.md). No existing file targets to verify.

    Repo Placement

    OK. Issue filed on ldraney/paldocs (listed as primary). Spike investigates across multiple repos (paldocs, pal-e-docs, pal-e-services, pal-e-deployments) -- appropriate to file on the primary consumer repo. No Keycloak/OIDC code exists in the paldocs repo yet (confirmed via grep), consistent with spike being investigative.

    Dependencies

    • [x] #22 (Deploy paldocs to production) -- documented in issue body Related section. Currently in next_up. Auth spike findings will inform deployment configuration (public vs gated). Dependency relationship is clear.
    • [x] #1344 (Postgres trigger) -- in_progress, no dependency conflict.
    • [x] Prior art (arch-rails-app) -- documented in issue body Related section. pal-enterprises already implements omniauth + Keycloak OIDC pattern.

    Acceptance Criteria

    Spike deliverables are clear and agent-verifiable: (1) docs/auth-visibility-architecture.md merged via docs-only PR, (2) follow-up tickets created on board-paldocs. Time-box of 1 session provides a hard stop. Completion can be verified by checking for the doc file and new board items.

    Blast Radius

    Low for the spike itself (investigation only, no code changes). Decisions made here will have high downstream impact on follow-up implementation tickets, but that blast radius is appropriately deferred. No existing auth/visibility code in the paldocs repo to conflict with.

    Decomposition Assessment

    No decomposition needed. Time-boxed investigation spike with a single deliverable doc and follow-up ticket creation. 1 session, 1 primary output. Well within the 5-minute rule for agent execution.

    Prior Review Issues -- Resolution Status

    • [x] RESOLVED: story-paldocs-auth note created -- contains Superadmin and Member user stories with proper scope and related links
    • [x] RESOLVED: arch-keycloak-paldocs stub note created -- contains pending decisions, prior art references (arch-keycloak, arch-rails-app, sop-keycloak-client-creation), and spike linkage
    • [x] RESOLVED: Issue body updated with #22 dependency -- Referenced in Related section with context about auth decisions affecting deployment
    • [x] RESOLVED: Issue body updated with arch-rails-app prior art -- Referenced in Related section as starting point for investigation

    Recommendation

    No action needed. All prior review issues have been addressed. Ticket is ready to advance.

  • Verdict: APPROVED

    Re-review of board item #1385. Prior review review-1385-2026-06-07 found 1 issue (missing arch-rails-views note). That issue has been resolved.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, references 482e8e7
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- As a superadmin, I want to read note content directly on the project page
    • [x] Context -- Explains page_note pattern extension, html_content from pal-e-docs
    • [x] File Targets -- 3 targets + 1 exclusion
    • [x] Feature Flag -- none (UI enhancement, appropriate)
    • [x] Acceptance Criteria -- 5 criteria
    • [x] Test Expectations -- integration + unit + run command
    • [x] Constraints -- Turbo/Stimulus, sanitize, collapsed-by-default
    • [x] Checklist -- standard 3-item
    • [x] Related -- project-paldocs, issue #12

    Traceability

    • [x] story:project-pages label -- present on board item #1385
    • [x] story note verified -- story-paldocs-project-pages listed in project-paldocs user-stories section
    • [x] arch:rails-views label -- present on board item #1385
    • [x] arch note verified -- arch-rails-views exists in pal-e-docs (note_type: doc, project: paldocs, title: "Architecture: Rails View Conventions"). Contains sections: ERB Templates, Sanitized HTML Rendering, Project Page Structure.
    • [x] Forgejo issue -- ldraney/paldocs#24, state: open

    File Targets

    • [x] app/views/projects/show.html.erb -- verified: exists (77 lines). Currently renders notes as title+slug links grouped by type (lines 36-54). No expand/collapse mechanism exists yet.
    • [x] app/assets/stylesheets/ -- verified: directory exists with application.css (20k). No inline-note-content styles exist yet.
    • [x] app/controllers/projects_controller.rb -- verified: exists (14 lines). Already includes :notes in eager loading (line 7). html_content is a text column on the notes table, loads automatically with Note records -- no additional includes needed.
    • [x] app/views/blocks/ (exclusion) -- verified: directory exists with _block.html.erb. Exclusion is correct -- blocks partial is for structured block rendering of page_note only.

    Repo Placement

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

    Dependencies

    • Board item #1343 (issue #12, "render project page note content on project show page") -- done. Prerequisite page_note rendering work. Completed. No blocker.
    • Board item #1344 (issue #13, "add Postgres trigger to bump projects.updated_at") -- in_progress. Not a direct dependency (sorting, not rendering). Same story but no blocking relationship.
    • Board item #1386 (issue #25, "collapsible accordion sections sorted by most recently modified") -- backlog. Sibling feature in same story. Accordion/expand UI from #25 may overlap with expand/collapse in this ticket. Not a blocker, but agents should be aware of potential UI pattern convergence.

    Acceptance Criteria

    5 criteria -- all verifiable by an agent:

    • AC1: Expand note to show html_content -- testable via integration test
    • AC2: Collapsed shows title + slug -- testable (current behavior)
    • AC3: Expanded renders HTML safely (sanitized) -- testable, sanitize helper already used in blocks partial (lines 23, 33)
    • AC4: No-content placeholder -- testable with nil html_content
    • AC5: External pal-e-docs link still available -- testable

    Test commands are valid (bundle exec rspec spec/). Existing Stimulus controllers (kanban_controller.js) confirm Hotwire patterns are established.

    Blast Radius

    • html_content is a text column that can contain arbitrary HTML from pal-e-docs. Ticket correctly requires sanitization. Existing sanitize pattern in app/views/blocks/_block.html.erb (lines 23, 33) provides the reference pattern with allowed_tags and allowed_attributes.
    • No other views currently render html_content directly -- this is net-new rendering surface.
    • Turbo Frames / Stimulus: the app already uses Hotwire (turbo-rails + stimulus imported in application.js, kanban_controller.js exists). Adding a new Stimulus controller for expand/collapse is consistent with the existing architecture.

    Decomposition Assessment

    3 file targets in 1 repo. 5 acceptance criteria. Estimated agent work: ~3-4 minutes. No decomposition needed.

    Recommendation

    No action needed. Prior issue resolved.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, references 482e8e7
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- As a superadmin, I want to read note content directly on the project page
    • [x] Context -- Explains page_note pattern extension, html_content from pal-e-docs
    • [x] File Targets -- 3 targets + 1 exclusion
    • [x] Feature Flag -- none (UI enhancement, appropriate)
    • [x] Acceptance Criteria -- 5 criteria
    • [x] Test Expectations -- integration + unit + run command
    • [x] Constraints -- Turbo/Stimulus, sanitize, collapsed-by-default
    • [x] Checklist -- standard 3-item
    • [x] Related -- project-paldocs, issue #12

    Traceability

    • [x] story:project-pages label -- present on board item #1385
    • [x] story note verified -- story-paldocs-project-pages exists in pal-e-docs AND listed in project-paldocs user-stories section
    • [x] arch:rails-views label -- present on board item #1385
    • [ ] arch note MISSING -- [SCOPE] No arch-rails-views note found in pal-e-docs. Create architecture note arch-rails-views for the Rails views component.
    • [x] Forgejo issue -- ldraney/paldocs#24, state: open

    File Targets

    • [x] app/views/projects/show.html.erb -- verified: exists (2.7k, 77 lines). Currently renders notes as title+slug links grouped by type (lines 36-54). No expand/collapse mechanism exists yet.
    • [x] app/assets/stylesheets/ -- verified: directory exists with application.css (20k). No inline-note-content styles exist yet.
    • [x] app/controllers/projects_controller.rb -- verified: exists (14 lines). Already includes :notes in eager loading (line 7). html_content is a text column on the notes table, so it loads automatically with the Note records -- no additional includes needed.
    • [x] app/views/blocks/ (exclusion) -- verified: directory exists with _block.html.erb. Exclusion is correct -- blocks partial is for structured block rendering of page_note only.

    Repo Placement

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

    Dependencies

    • Board item #1343 (issue #12, "render project page note content on project show page") -- done. This is the prerequisite page_note rendering work. Completed. No blocker.
    • Board item #1344 (issue #13, "add Postgres trigger to bump projects.updated_at") -- in_progress. Not a direct dependency for this ticket (it's about sorting, not rendering), but same story.
    • Board item #1386 (issue #25, "collapsible accordion sections sorted by most recently modified") -- backlog. This is a sibling feature in the same story. The accordion/expand UI from #25 may overlap with the expand/collapse mechanism in this ticket. Not a blocker, but agents should be aware of potential UI pattern convergence.
    • No dependencies documented in the issue scope. The Lineage section references 482e8e7 (link notes to pal-e-docs) which is merged.

    Acceptance Criteria

    5 criteria -- all are verifiable by an agent:

    • AC1: Expand note to show html_content -- testable via integration test
    • AC2: Collapsed shows title + slug -- testable (current behavior)
    • AC3: Expanded renders HTML safely (sanitized) -- testable, sanitize helper already used in blocks partial
    • AC4: No-content placeholder -- testable with nil html_content
    • AC5: External pal-e-docs link still available -- testable

    Test commands are valid (bundle exec rspec spec/). No existing specs exist beyond helpers -- agent will need to create specs from scratch.

    Blast Radius

    • html_content is a text column that can contain arbitrary HTML from pal-e-docs. The ticket correctly requires sanitization. The existing sanitize pattern in app/views/blocks/_block.html.erb (line 23) provides the reference pattern with allowed_tags and allowed_attributes.
    • No other views currently render html_content directly -- this is net-new rendering surface.
    • Turbo Frames / Stimulus: the app already uses Hotwire (turbo-rails + stimulus imported in application.js, kanban_controller.js exists). Adding a new Stimulus controller for expand/collapse is consistent with the existing architecture.

    Decomposition Assessment

    3 file targets in 1 repo. 5 acceptance criteria. Estimated agent work: ~3-4 minutes. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails-views for the Rails views component. This label is used by 3 board items (#1220, #1342, #1343 -- all done) plus this ticket. The component deserves a backing arch note documenting view conventions, partial structure, and Hotwire patterns.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, UX improvement
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present, well-formed
    • [x] Context -- present, clear motivation
    • [x] File Targets -- 4 targets listed + exclusions
    • [x] Feature Flag -- "none" (appropriate for CSS/view-only change)
    • [x] Acceptance Criteria -- 7 criteria listed
    • [x] Test Expectations -- 2 test expectations + run command
    • [x] Constraints -- present (HTML details, lightweight, accessible)
    • [x] Checklist -- present
    • [x] Related -- references project-paldocs and issue #13

    All required sections present per template-issue-feature.

    Traceability

    • [x] story:project-pages label -- present on board item
    • [ ] story note MISSING -- story-paldocs-project-pages is referenced in project-paldocs user-stories section but the note itself does not exist in pal-e-docs. [SCOPE] Create story note story-paldocs-project-pages.
    • [x] arch:rails-hotwire label -- present on board item
    • [ ] arch note MISSING -- no arch-rails-hotwire note found in pal-e-docs. [SCOPE] Create architecture note arch-rails-hotwire for the Stimulus/Turbo component.
    • [x] Forgejo issue -- ldraney/paldocs#25, state: open

    File Targets

    • [x] app/views/projects/show.html.erb -- verified: exists, contains flat note-group lists (lines 36-54) and flat repo-list (lines 57-76) that need accordion conversion
    • [x] app/controllers/projects_controller.rb -- verified: exists, line 8 sorts by title (sort_by(&:title).group_by(&:note_type)), needs changing to updated_at: :desc
    • [x] app/assets/stylesheets/ -- verified: directory exists, application.css present, no existing accordion styles
    • [x] app/javascript/controllers/ -- verified: directory exists, Stimulus fully configured via importmap with eager loading. Existing controllers: hello_controller.js, kanban_controller.js. New accordion controller would register automatically.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are in the paldocs Rails app. Single-repo change.

    Dependencies

    • BLOCKER: Issue #13 (feat: add Postgres trigger to bump projects.updated_at) is still open and in_progress on the board (item #1344). The issue body claims "The trigger from #13 ensures projects.updated_at stays fresh. Sort order is ready." However, the migration exists at db/migrate/20260606120000_add_bump_project_updated_at_trigger.rb but the issue is not yet closed/merged. The accordion sorting depends on notes.updated_at (not projects.updated_at), so the core accordion feature can proceed, but the Related section's claim about #13 being done is inaccurate.
    • No other board items block this work.
    • Item #1385 (ldraney/paldocs#24) shares the story:project-pages label but has arch:rails-views -- no direct conflict.

    Acceptance Criteria

    • [x] AC 1-6: Verifiable -- accordion rendering, counts, sort order, and "show all" link can all be tested in integration tests
    • [ ] AC 7: "Accordion state persists during the page session (doesn't collapse on Turbo navigation)" -- this is testable but requires Turbo-aware system testing (Capybara + JS driver). The spec directory has no system/feature specs yet, only rails_helper.rb and spec_helper.rb. [BODY] Clarify whether AC 7 requires a system test or just a Stimulus implementation note (e.g., Turbo permanent attribute).

    Blast Radius

    Low. The accordion change is isolated to the project show page. The flat-list pattern (note-group, note-list, repo-list CSS classes) is used only in projects/show.html.erb. No other views share these class names. No downstream consumers affected.

    Decomposition Assessment

    • 4 file targets in 1 repo -- OK
    • 7 acceptance criteria -- borderline but manageable since they are tightly related (all accordion behavior)
    • Estimated agent work: ~3-4 minutes (view template + controller query change + CSS + optional Stimulus controller)
    • No decomposition needed.

    Recommendations

    • [SCOPE] Create story note story-paldocs-project-pages -- referenced in project-paldocs user-stories section but note does not exist
    • [SCOPE] Create architecture note arch-rails-hotwire for the Hotwire/Stimulus/Turbo component
    • [BODY] Clarify AC 7 (Turbo persistence): specify implementation approach (e.g., data-turbo-permanent attribute on accordion containers) and whether a system test is expected given no existing system test infrastructure
    • [BODY] Update dependency note: issue #13 is still in_progress, not complete. The accordion sorting uses notes.updated_at (which already exists in the DB) so the feature is not actually blocked, but the Related section should reflect accurate status
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Spike
    • [x] Lineage -- Standalone, operational need stated
    • [x] Repo -- Multiple repos listed with primary identified
    • [x] Question -- Clear top-level question with 8 sub-questions
    • [x] Deliverables -- docs/auth-visibility-architecture.md + follow-up tickets
    • [x] Time-box -- 1 session
    • [x] Related -- project, SOP, and board referenced

    Traceability

    • [x] story:auth label present
    • [ ] story note MISSING -- [SCOPE] No "story:auth" entry exists in project-paldocs user-stories section. Create user story entry on project-paldocs.
    • [x] arch:keycloak label present
    • [ ] arch note MISSING -- [SCOPE] arch-keycloak note does not exist in pal-e-docs. This is a recurring gap flagged in at least 3 prior reviews (review-1141, review-938, review-1182). Create architecture note arch-keycloak for the Keycloak component.
    • [x] Forgejo issue -- ldraney/paldocs#23, open
    • [x] sop-keycloak-client-creation -- referenced and verified to exist (active SOP)

    File Targets

    N/A -- Spike type. Deliverable is a new doc to be created (docs/auth-visibility-architecture.md). No existing file targets to verify.

    Repo Placement

    OK. Issue filed on ldraney/paldocs (listed as primary). Spike is investigative across multiple repos (paldocs, pal-e-docs, pal-e-services, pal-e-deployments) -- appropriate to file on the primary consumer repo.

    Dependencies

    • Item #1369 "Deploy paldocs to production" is in next_up. Auth decisions from this spike should inform the deployment configuration (whether to gate behind Keycloak before going public). This dependency is NOT documented in the issue body.
    • Item #1344 "Postgres trigger" is in_progress -- no dependency conflict.
    • Prior art exists: pal-enterprises Rails app already uses omniauth + Keycloak (see arch-rails-app). The spike should reference this as a starting point.

    Acceptance Criteria

    Spike deliverables are clear and verifiable: (1) docs/auth-visibility-architecture.md merged via docs-only PR, (2) follow-up tickets created. Time-box of 1 session provides a hard stop. An agent can verify completion by checking for the doc file and new board items.

    Blast Radius

    Low for the spike itself (investigation only). The decisions made here will have high blast radius on follow-up implementation -- affecting pal-e-docs API (visibility filtering), paldocs Rails app (auth middleware), pal-e-services (Terraform Keycloak client), and pal-e-deployments (secrets). But that blast radius is appropriately deferred to follow-up tickets.

    Decomposition Assessment

    No decomposition needed. This is a time-boxed investigation spike with a single deliverable doc. 1 session, 1 primary output. Well within the 5-minute rule for agent execution.

    Recommendations

    • [SCOPE] Create user story entry "story:auth" on project-paldocs user-stories section. Suggested text: "As an admin, I can control which notes are visible to members vs public, so that sensitive operational docs remain private."
    • [SCOPE] Create architecture note arch-keycloak for the Keycloak component. This is a platform-wide gap that has been flagged in 3+ prior reviews. Should cover: realm structure, client pattern, role hierarchy, integration conventions.
    • [BODY] Add dependency note to Related section: "board-paldocs #1369 (Deploy paldocs to production) -- auth spike findings should inform deployment config (public vs gated)."
    • [BODY] Add reference to prior art: "arch-rails-app (pal-enterprises) -- existing Rails + omniauth-keycloak pattern to reference."
  • Review: Deploy paldocs to production review-1369-2026-06-06-r3

    Verdict: APPROVED

    Round 3 review (2026-06-06). Prior reviews (review-1369-2026-06-06) returned false positives on two points: (1) claiming service-onboarding-sop does not exist, and (2) claiming bin/docker-entrypoint skips db:migrate. Both were incorrect. This review corrects those findings and re-evaluates the full scope.

    Prior Findings Corrections

    Prior Finding Correction
    [BODY] service-onboarding-sop "does not exist" FALSE POSITIVE -- service-onboarding-sop exists in pal-e-docs (note_type: sop, status: active, project: pal-e-platform). The issue's reference is valid.
    [BODY] AC #6 "db:migrate is factually wrong" FALSE POSITIVE -- The local working tree has stale uncommitted changes that revert PR #21. The committed version (git show HEAD:bin/docker-entrypoint) correctly runs ./bin/rails db:migrate when the command is bundle or ./bin/rails. The issue's reference to "db:migrate runs on pod startup via entrypoint (PR #21)" is accurate.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- references #15 CI/CD infrastructure and service-onboarding-sop gap
    • [x] Repo -- ldraney/pal-e-services (primary), ldraney/pal-e-platform (NetworkPolicy)
    • [x] User Story -- present as narrative
    • [x] Context -- thorough, includes "what exists" and "what's missing" with SOP step mapping
    • [x] File Targets -- 3 targets listed in table format (1 partially stale, see below)
    • [x] Acceptance Criteria -- 9 criteria listed, all verifiable
    • [x] Test Expectations -- 5 manual verification steps
    • [x] Constraints -- 4 constraints documented, all accurate
    • [x] Checklist -- 11 ordered steps
    • [x] Related -- references issues, SOPs, PRs
    • [x] Pre-Deploy Validation -- bonus section maps SOP checklist items

    All required sections for a Feature template are present.

    Traceability

    • [x] story:infra label -- present on board item
    • [ ] story note MISSING -- No story-paldocs-infra entry exists in project-paldocs user-stories section. Only story-paldocs-kanban and story-paldocs-project-pages are listed. Infrastructure/deployment is foundational work -- acceptable to defer, but note should exist for traceability. [SCOPE] Create user story entry story-paldocs-infra on project-paldocs user-stories section.
    • [x] arch:ci-cd label -- present on board item
    • [ ] arch note MISSING -- No arch-ci-cd note exists in pal-e-docs. Search returned zero results. [SCOPE] Create architecture note arch-ci-cd for CI/CD component.
    • [x] Forgejo issue -- ldraney/paldocs#22, state: open

    File Targets

    • [x] pal-e-services/terraform/k3s.tfvars -- verified: file exists at /home/ldraney/pal-e-services/terraform/k3s.tfvars. paldocs is NOT yet in the services map. The proposed var.services entry follows the landscaping-assistant pattern correctly (port 3000, funnel true, source_repo/source_path for pal-e-deployments overlay).
    • [x] pal-e-platform/terraform/network-policies.tf -- verified: file exists. NOTE: paldocs namespace is ALREADY present in the postgres NetworkPolicy allowlist. This file target is already satisfied. The issue says "Add paldocs namespace to postgres NetworkPolicy" but this work appears to be done. Minor scope inaccuracy -- the checklist step and AC #5 referencing this can be checked off immediately. Not blocking.
    • [x] paldocs-secrets (k8s Secret) -- manual kubectl creation. Correctly documented as "Create manually" with keys SECRET_KEY_BASE + DATABASE_URL. The deployment patch in pal-e-deployments/overlays/paldocs/prod/ references paldocs-secrets with envFrom. Constraint about "secrets before sync" is correctly documented and matches service-onboarding-sop step 3.

    Repo Placement

    Issue is filed on ldraney/paldocs. Primary work is on ldraney/pal-e-services (add to var.services + tofu apply). Secondary work on ldraney/pal-e-platform (NetworkPolicy) appears already complete. This is a deployment/ops ticket -- filing on the service repo being deployed is acceptable. No repo mismatch.

    Dependencies

    • #15 (CI/CD infrastructure) -- DONE. Board item #1359 in done column. Pipeline builds images and updates kustomize tags via .woodpecker.yaml.
    • #14 (Infra spike) -- DONE. Board item #1345 in done column.
    • pal-e-deployments PR #179 -- Merged. Kustomize overlay exists at overlays/paldocs/prod/kustomization.yaml. Verified: namespace set to paldocs, containerPort 3000, image reference harbor.tail5b443a.ts.net/paldocs/app.
    • Dockerfile -- EXPOSE 3000 confirmed, matches var.services port and kustomize overlay.
    • bin/docker-entrypoint -- Committed version (HEAD) includes db:migrate guard from PR #21. Correctly handles shared database.
    • No blocking items on board. All upstream dependencies are resolved.

    Acceptance Criteria

    • [x] AC1: paldocs entry in var.services -- verifiable via grep
    • [x] AC2: tofu plan shows clean diff -- verifiable, requires Lucas approval
    • [x] AC3: tofu apply succeeds -- verifiable, requires Lucas approval
    • [x] AC4: paldocs-secrets exists before ArgoCD sync -- verifiable via kubectl
    • [x] AC5: NetworkPolicy allows paldocs -> postgres -- already satisfied (paldocs in netpol allowlist)
    • [x] AC6: kubectl get application shows Synced + Healthy -- verifiable
    • [x] AC7: Pod running, /up returns 200 -- verifiable
    • [x] AC8: https://paldocs.tail5b443a.ts.net serves projects index -- verifiable
    • [x] AC9: CI merge triggers full pipeline -- verifiable end-to-end

    All 9 acceptance criteria are verifiable. No factual errors found (correcting prior review's false positive on db:migrate).

    Blast Radius

    • Shared database: paledocs DB shared with pal-e-docs FastAPI. DATABASE_URL must use same credentials. Correctly documented.
    • CI registry URL: .woodpecker.yaml uses external Harbor URL (harbor.tail5b443a.ts.net) while SOP and landscaping-assistant use internal (harbor.harbor.svc.cluster.local). This is a pre-existing issue from PR #21, not in scope for this deployment ticket. Worth a follow-up issue but not blocking deployment.
    • Port consistency: Dockerfile EXPOSE 3000 = var.services port 3000 = kustomize containerPort 3000 = service targetPort 3000. All aligned.
    • No downstream consumers: paldocs is a UI service. Deployment only affects end-user access.

    Decomposition Assessment

    3 file targets across 2 repos (one already done). 9 acceptance criteria but they are sequential ops steps in a single deployment workflow, not parallel agent code changes. This is a human-executed ops ticket with tofu apply requiring Lucas approval. The 5-minute rule for agent decomposition does not apply to manual infrastructure deployment. The issue checklist already provides ordered sub-steps.

    No decomposition needed.

    Recommendations

    • [BODY] Update file target #2: pal-e-platform/terraform/network-policies.tf is listed as "Update -- Add paldocs namespace to postgres NetworkPolicy allowlist" but paldocs is already in the allowlist. Update to reflect this is already done, or remove from file targets.
    • [BODY] Update checklist item "Add paldocs namespace to postgres NetworkPolicy" -- mark as already complete or note it was done previously.
    • [SCOPE] Create user story entry story-paldocs-infra on project-paldocs user-stories section. (Foundational work -- acceptable to defer but note should exist for traceability.)
    • [SCOPE] Create architecture note arch-ci-cd for CI/CD component. (Label exists on board item but backing note is missing.)

    Minimum for APPROVED: The [BODY] items are minor inaccuracies (stale file target for already-completed NetworkPolicy work) that do not block execution. The [SCOPE] items are deferrable for infrastructure work. The ticket scope is solid, all remaining work is clearly defined, and all acceptance criteria are verifiable. APPROVED for execution.

  • Review: Deploy paldocs to production review-1369-2026-06-06

    Verdict: NEEDS_REFINEMENT

    Re-review (2026-06-06) after refinement attempt. The issue body was updated but key findings from the first review remain unaddressed.

    Prior Findings Status

    Prior Finding Status
    [BODY] AC #6 db:migrate incorrect NOT FIXED -- issue still says "db:migrate runs on pod startup via entrypoint (PR #21 wired this)"
    [BODY] ArgoCD Application location unclear NOT ADDRESSED
    [BODY] harbor-creds pattern unclear PARTIALLY -- checklist item added to investigate, acceptable for ops ticket
    [SCOPE] story-paldocs-infra missing NOT CREATED
    [SCOPE] arch-ci-cd missing NOT CREATED
    [SCOPE] sop-secrets-management reference PARTIALLY -- changed to service-onboarding-sop, which also does not exist
    [DECOMPOSE] 7 AC across 3 repos Repo scope reduced from 3 to 2. See decomposition assessment below.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- references #15 CI/CD and #14 infra spike
    • [x] Repo -- ldraney/paldocs (primary), ldraney/pal-e-deployments
    • [x] User Story -- present as narrative
    • [x] Context -- thorough, includes what exists and what's missing
    • [x] File Targets -- 5 resources listed with actions (table format, clear)
    • [x] Acceptance Criteria -- 8 criteria listed (1 is factually incorrect)
    • [x] Test Expectations -- present
    • [x] Constraints -- 4 constraints documented
    • [x] Checklist -- 9 items
    • [x] Related -- references to issues and SOPs

    All required sections for a Feature template are present.

    Traceability

    • [x] story:infra label -- present on board item
    • [ ] story note MISSING -- No story-paldocs-infra note exists in pal-e-docs. The project-paldocs user-stories section only lists story-paldocs-kanban and story-paldocs-project-pages. Infrastructure/deployment work is foundational -- acceptable to defer, but the note should still be created. [SCOPE] Create user story entry story-paldocs-infra on project-paldocs user-stories section.
    • [x] arch:ci-cd label -- present on board item
    • [ ] arch note MISSING -- No arch-ci-cd note exists in pal-e-docs. Search returned zero results. [SCOPE] Create architecture note arch-ci-cd for CI/CD component.
    • [x] Forgejo issue -- ldraney/paldocs#22, state: open

    File Targets

    • [x] paldocs namespace -- Create. No namespace exists yet. Kustomization at pal-e-deployments/overlays/paldocs/prod/kustomization.yaml sets namespace: paldocs. Must be created before ArgoCD Application (per CreateNamespace=false constraint). Correctly documented.
    • [x] paldocs-secrets Secret -- Create. Deployment patch references paldocs-secrets with keys SECRET_KEY_BASE and DATABASE_URL. The env var mapping is correct: secret key DATABASE_URL maps to env var PALDOCS_DATABASE_URL, which matches config/database.yml production config.
    • [x] harbor-creds Secret -- Create. The base deployment template references harbor-creds imagePullSecrets. Issue now includes a checklist step to investigate the provisioning pattern from landscaping-assistant. Acceptable for an ops ticket.
    • [x] ArgoCD Application -- Create. Reference pattern from landscaping-assistant is included in the issue body with full YAML. Clear enough for execution.
    • [x] Tailscale Ingress -- Create. Production URL target documented.

    Repo Placement

    Improved from first review. Issue now lists 2 repos instead of 3 (removed pal-e-platform). The issue is filed on ldraney/paldocs which is the service being deployed. Actual work is primarily cluster-level kubectl operations plus potentially an ArgoCD Application manifest in pal-e-deployments. Placement is acceptable for a deployment ticket.

    Dependencies

    • #15 (CI/CD infrastructure) -- DONE. Board item #1359 is in done column. Pipeline builds images and updates kustomize tags. Verified: .woodpecker.yaml has build-and-push + update-kustomize-tag steps.
    • #14 (Infra spike) -- DONE. Board item #1345 is in done column.
    • pal-e-deployments PR #179 -- Merged. Overlay files exist.
    • Terraform DB secret -- exists as paledocs-db-url in pal-e-app namespace.
    • No blocking items found on the board. This ticket has no upstream blockers.

    Acceptance Criteria

    • [x] kubectl get ns paldocs -- verifiable
    • [x] kubectl get secret paldocs-secrets -n paldocs -- verifiable
    • [x] kubectl get secret harbor-creds -n paldocs -- verifiable (new AC, addresses harbor-creds gap)
    • [x] kubectl get application paldocs -n argocd -- verifiable
    • [x] Pod running + /up health check -- verifiable
    • [ ] AC #6 STILL INCORRECT: "db:migrate runs on pod startup via entrypoint (PR #21 wired this)" -- The bin/docker-entrypoint explicitly skips db:prepare with comment: "Shared database -- Rails does not manage migrations. Skip db:prepare; tables already exist (managed by pal-e-docs FastAPI)." The entrypoint just calls exec "${@}". This AC must be removed or reworded. [BODY] Replace AC #6 with: "Entrypoint starts cleanly without migration errors (shared DB, Python-managed schema)."
    • [x] Production URL resolves -- verifiable
    • [x] CI merge triggers full pipeline -- verifiable end-to-end

    Blast Radius

    • Shared database: The paledocs database is shared between pal-e-docs (FastAPI, Python-managed schema) and this Rails app (read + triggers). The DATABASE_URL secret must use the same credentials. Correctly documented in constraints.
    • harbor-creds pattern: Cross-cutting concern for namespace provisioning. Adequately flagged as investigation item.
    • paldocs-dev namespace: Teardown after prod verification. Low risk -- already 502.
    • No downstream consumers: paldocs is a UI service. Deployment only affects end-user access.

    Decomposition Assessment

    Revised assessment: The first review flagged decomposition based on 7 AC across 3 repos. The updated issue reduced to 2 repos and the work is primarily sequential kubectl operations (namespace -> secrets -> ArgoCD app -> ingress -> verify). This is a manual ops deployment, not agent-driven code changes. The 5-minute rule targets agent code passes. For human-executed ops work, these are sequential steps in a single deployment workflow -- decomposing into separate board items would create unnecessary ticket overhead.

    No decomposition needed. The checklist in the issue body already provides the ordered sub-steps. This is appropriate for an ops ticket.

    Recommendations

    Remaining items that must be addressed before READY:

    • [BODY] Fix AC #6: Remove "db:migrate runs on pod startup via entrypoint (PR #21 wired this)". Replace with: "Entrypoint starts cleanly without attempting db:migrate (shared DB, Python-managed schema)." The bin/docker-entrypoint explicitly skips migrations -- this AC is factually wrong and would cause confusion during validation.
    • [BODY] Fix Related section: service-onboarding-sop does not exist in pal-e-docs. Either create the SOP or remove the reference to avoid dangling links.
    • [SCOPE] Create user story entry story-paldocs-infra on project-paldocs user-stories section. (Foundational work -- acceptable to defer but note should exist for traceability.)
    • [SCOPE] Create architecture note arch-ci-cd for CI/CD component. (Label exists on board item but backing note is missing.)

    Minimum for READY: Fix AC #6 [BODY]. The [SCOPE] items are deferrable for infrastructure work but should be created before the ticket moves to done. The service-onboarding-sop reference [BODY] is a minor cleanup.

  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, discovery context provided
    • [x] Repo -- ~/claude-custom (local-only, correctly noted)
    • [x] User Story -- As a platform operator...
    • [x] Context -- Explains the worktree isolation gap and agent misbehavior
    • [x] File Targets -- 3 modify targets, 2 do-not-touch targets
    • [x] Acceptance Criteria -- 6 criteria
    • [x] Test Expectations -- manual verification steps and run command
    • [x] Constraints -- 3 constraints documented
    • [x] Checklist -- present
    • [x] Related -- project and arch references

    Traceability

    • [x] story:platform label -- "As a platform operator I want worktree isolation conventions and hooks consistently enforced"
    • [ ] story note MISSING -- [SCOPE] No "story:platform" entry exists in project-paldocs user-stories section. The project page only has story-paldocs-kanban and story-paldocs-project-pages. This is cross-cutting platform work that arguably belongs on project-pal-e-platform rather than project-paldocs. However, the ticket is tracked on board-paldocs per the "cross-cutting platform concern" rationale. Acceptable as foundational work -- story note creation is recommended but not blocking.
    • [x] arch:hooks label -- hooks infrastructure
    • [ ] arch note MISSING -- [SCOPE] No arch-hooks note found in pal-e-docs. Recommended to create architecture note documenting the hooks system. Not blocking for this ticket.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/paldocs/issues/20, open

    File Targets

    • [x] CLAUDE.md -- verified exists on main. Branch fix/worktree-isolation-convention has updated version with proper claude-custom (branch checkout) vs all-others (clone to /tmp) distinction. 14 lines added, 5 removed. Content is correct.
    • [x] hooks/cross-repo-isolation.sh -- verified exists. Lines 15, 65, 75 still contain stale "worktree isolation" / "spawning repo uses worktree isolation automatically" references. These need updating per ticket scope.
    • [x] hooks/cleanup-worktrees.sh -- verified exists. $HOME/paldocs is NOT in the REPO_DIRS array (confirmed by grep). Needs adding per ticket scope.
    • [x] hooks/block-claude-custom-main-edit.sh -- verified exists (do-not-touch, correctly excluded)
    • [x] hooks/check-claude-custom-clean.sh -- verified exists (do-not-touch, correctly excluded)

    Repo Placement

    OK. The issue is filed on ldraney/paldocs (Forgejo) but targets ~/claude-custom (local-only). This is correctly documented in the issue body: "local-only, no Forgejo remote -- tracked on paldocs board as cross-cutting platform concern." No mismatch.

    Dependencies

    • Branch fix/worktree-isolation-convention already exists with CLAUDE.md changes -- agent should build on this branch rather than creating a new one.
    • Issue mentions "pre-existing uncommitted changes on main" for block-main-commits.sh, plugins/installed_plugins.json, settings.json. However, git status on main currently shows clean working tree. These were either already resolved or the issue body is stale on this point. AC #5 ("Pre-existing uncommitted changes on main are committed or intentionally discarded") is effectively pre-satisfied.
    • No blocking dependencies from other board items. #1359 (CI/CD infrastructure gaps) is in backlog and unrelated.

    Acceptance Criteria

    All 6 AC are verifiable by an agent:

    • AC 1-4: grep/read verification of specific file content changes
    • AC 5: git status check -- already satisfied (main is clean)
    • AC 6: git log/branch check -- verifiable

    Test expectations include manual steps and a run command. The run command references "test-input.json" which does not exist -- agent would need to craft it. This is minor and within agent capability.

    Blast Radius

    • hooks/pre-spawn-freshness.sh line 5 has a comment referencing "worktree isolation" -- cosmetic, not functional. Not in scope but worth noting.
    • No other hooks reference the stale "isolation: worktree" pattern.
    • The CLAUDE.md change on the branch also gets symlinked to ~/.claude/CLAUDE.md, so the convention update will propagate to all sessions automatically.
    • No downstream consumers affected beyond agent behavior alignment.

    Decomposition Assessment

    3 file targets in 1 repo, 6 AC, estimated agent work ~3-4 minutes. Branch already exists with partial work done (CLAUDE.md). No decomposition needed -- fits single agent pass.

    Recommendation

    • [BODY] Update AC #5 to note that main is already clean: "Pre-existing uncommitted changes on main have been resolved (main is clean as of 2026-06-06)." Or remove this AC since it is pre-satisfied.
    • [SCOPE] Create user story entry for "story:platform" on project-paldocs user-stories section (or move ticket to project-pal-e-platform board if that is the correct home for platform-wide concerns).
    • [SCOPE] Create architecture note arch-hooks documenting the hooks system in claude-custom.

    None of the above are blocking. The scope is solid and the ticket is implementable as-is.

  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, discovered during project sort debugging
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- present and well-formed
    • [x] Context -- thorough, explains DDL ownership change
    • [x] File Targets -- 3 modify/create targets, 1 do-not-touch target
    • [x] Acceptance Criteria -- 9 criteria
    • [x] Test Expectations -- 4 manual tests + 1 run command
    • [x] Constraints -- 5 constraints including shared DB safety
    • [x] Checklist -- present
    • [x] Related -- 4 references including arch note and story note

    Traceability

    • [x] story:project-pages label -- present on board item
    • [x] story note verified -- story-paldocs-project-pages exists in pal-e-docs, references issue #13 in its scope list
    • [x] arch:rails-db label -- present on board item
    • [x] arch note verified -- arch-rails-db exists in pal-e-docs, documents DDL ownership conventions
    • [x] Forgejo issue -- ldraney/paldocs#13, open

    Minor note: The project-paldocs user-stories section still says "No user-story notes created yet" despite story-paldocs-project-pages existing. This is a housekeeping item, not a blocker for this ticket.

    File Targets

    • [x] db/migrate/YYYYMMDD_add_project_activity_trigger.rb -- NEW FILE. The db/migrate/ directory does not yet exist (only db/seeds.rb present). Agent will need to create the directory. Consistent with the DDL ownership change described in the ticket.
    • [x] bin/docker-entrypoint -- verified exists. Currently skips migrations with comment "Shared database -- Rails does not manage migrations." Agent must update to run db:migrate before exec.
    • [x] docs/data-model.md -- verified exists. Currently states "No db/schema.rb or db/migrate/ in this repo" in the Shared Database section. Agent must update to document the trigger and DDL ownership change.
    • [x] app/controllers/projects_controller.rb -- verified DO NOT TOUCH. Sort query Project.order(updated_at: :desc) is already correct on line 3.

    Repo Placement

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

    Dependencies

    • #14 (infra spike) -- referenced in Related as documenting the entrypoint pattern. Currently in_progress on the board. This ticket modifies bin/docker-entrypoint to add db:migrate. If #14 also modifies the entrypoint, there could be a merge conflict, but both changes are additive. Not a hard blocker -- the changes are compatible.
    • #12 (render project page note content) -- sibling ticket in story:project-pages, currently in next_up. No code overlap.

    Acceptance Criteria

    All 9 criteria are testable. The migration reversibility criterion is verifiable with bin/rails db:migrate:down. The manual tests are clear. The "Python pal-e-docs layer is unaffected" criterion is verifiable by writing a note via MCP after deploy.

    One note: "Projects index sorts by actual activity after deploy" is an integration criterion that depends on the trigger actually firing in production. This is reasonable for a 3-point ticket.

    Blast Radius

    Low. The trigger is DB-level and transparent to both Rails and Python. The only controller using updated_at sort is ProjectsController#index. No other models or controllers reference project ordering. The trigger fires only on notes table INSERT/UPDATE, which is the primary write path from pal-e-docs.

    No similar "stale updated_at" pattern found in other models -- this is specific to the projects/notes parent-child relationship.

    Decomposition Assessment

    3 file targets in 1 repo, 9 acceptance criteria. The criteria count is above 5 but the work is tightly scoped -- one migration file, one entrypoint line, one doc update. Estimated agent time: 3-4 minutes. No decomposition needed.

    Recommendation

    No action needed. Ticket is well-scoped and ready for implementation.

  • Verdict: NEEDS_REFINEMENT

    Re-review after refinement. Previous review: review-1344-2026-06-06 (NEEDS_REFINEMENT). The DDL ownership conflict -- the primary blocker -- has been resolved in the issue body. Two traceability notes claimed as created were not found in pal-e-docs.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, discovery context provided
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- properly formatted As/I want/So that
    • [x] Context -- clear motivation, solution approach explained, DDL ownership change explicitly documented
    • [x] File Targets -- 3 files to modify/create, 1 exclusion documented
    • [x] Acceptance Criteria -- 9 items, well-scoped (Python compatibility AC added since last review)
    • [x] Test Expectations -- 4 manual checks + run command
    • [x] Constraints -- shared DB, NULL handling, plpgsql, arch-rails-db reference
    • [x] Checklist -- present
    • [x] Related -- project + related issue #14 + arch note + story note linked

    Traceability

    • [x] story:project-pages label -- present on board item
    • [ ] story note MISSING -- story-paldocs-project-pages does not exist in pal-e-docs. The project-paldocs user-stories section still reads "No user-story notes created yet." The issue body references this note in the Related section, but it was not created. [SCOPE] Create story note story-paldocs-project-pages and add entry to project-paldocs user-stories section.
    • [x] arch:rails-db label -- present on board item
    • [ ] arch note MISSING -- arch-rails-db does not exist in pal-e-docs (search returned zero results). The issue body and Constraints section both reference this note, but it was not created. [SCOPE] Create architecture note arch-rails-db documenting the DDL ownership split (Python owns table creation, Rails owns supplementary DDL like triggers/indexes).
    • [x] Forgejo issue -- ldraney/paldocs#13, state: open

    File Targets

    • [x] db/migrate/YYYYMMDD_add_project_activity_trigger.rb -- db/migrate/ directory does not exist yet. This is expected: Rails will create it on first migration. The issue body now explicitly documents the DDL ownership change justifying Rails-managed migrations for triggers. config/application.rb already has dump_schema_after_migration = false and migration_error = false, which are compatible with this approach.
    • [x] bin/docker-entrypoint -- verified exists (6 lines). Currently contains comment "Shared database -- Rails does not manage migrations. Skip db:prepare." The entrypoint will need updating to run db:migrate and the comment updated. File is consistent with what the ticket describes.
    • [x] docs/data-model.md -- verified exists (129 lines). Contains "Shared Database" section (line 124) stating "all DDL is managed by the FastAPI pal-e-docs service" and "No db/schema.rb or db/migrate/ in this repo" (line 126). Both statements will need updating to reflect the DDL ownership change. File target is correct.
    • [x] app/controllers/projects_controller.rb -- verified: sort query is Project.order(updated_at: :desc) on line 3. Correctly excluded from modification targets.

    Repo Placement

    OK. Issue is filed on ldraney/paldocs (the Rails app). The DDL ownership change is now explicitly documented in the issue body -- Rails manages supplementary DDL (triggers, indexes) while Python continues to own table creation. This is a single-repo change.

    Dependencies

    • Issue #14 (spike: audit infra/pipeline gaps) is referenced as related. It is currently in_progress on the board (item #1345). The ticket says entrypoint pattern depends on this spike, but the trigger migration itself can proceed independently. No hard blocker.
    • Item #1343 (feat: render project page note content) shares the story:project-pages label and is in backlog. No conflict -- they are independent features.

    Acceptance Criteria

    9 ACs, all testable by an agent. The new AC #9 ("Python pal-e-docs layer is unaffected -- trigger is transparent at the DB level") addresses the previous review's concern about Python compatibility. Test expectations include manual verification of Python API writes. The bin/rails db:migrate run command is valid. ACs are complete and verifiable.

    Blast Radius

    • The Python pal-e-docs API writes heavily to the notes table (create, update, bulk operations via MCP). The Postgres trigger will fire on every Python write. The trigger is simple (bump parent project's updated_at) and should be transparent, but AC #9 and test expectation #4 now cover this verification.
    • Board items and blocks (children of notes) also represent project activity but are not covered by this trigger. This is acceptable scope -- the ticket focuses on notes as the primary activity signal. Future tickets could extend the trigger.

    Decomposition Assessment

    3 file targets in 1 repo, 9 acceptance criteria. The 9 ACs are at the threshold but the implementation is cohesive -- a single migration file, an entrypoint update, and a doc update. Estimated agent work is under 5 minutes. No decomposition needed.

    Recommendations

    • [SCOPE] Create architecture note arch-rails-db in pal-e-docs. The issue body and Constraints section reference it, but it does not exist. This note should document the DDL ownership split: Python owns table creation (Alembic), Rails owns supplementary DDL (triggers, indexes via db/migrate).
    • [SCOPE] Create story note story-paldocs-project-pages in pal-e-docs and add an entry to the project-paldocs user-stories section. The issue references this story, but neither the note nor the project page entry exist.
  • Verdict: APPROVED

    Re-review of board item #1343 after refinement. Previous review: review-1343-2026-06-06 (NEEDS_REFINEMENT). All blocking findings resolved.

    Previous Findings Status

    • [x] [BODY] N+1 eager-load note -- FIXED. Context section now explicitly states: "The controller's show action currently does Project.includes(:notes, :repos) but does NOT include :page_note." New AC #10 added: "No N+1 queries -- page_note and its blocks are eager-loaded."
    • [x] [SCOPE] story note story-paldocs-project-pages -- CREATED. Note exists in pal-e-docs (note_type: doc, project: paldocs). Title: "User Story: Project Pages".
    • [x] [SCOPE] arch note arch-rails-views -- CREATED. Note exists in pal-e-docs (note_type: doc, project: paldocs). Title: "Architecture: Rails Views".

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone
    • [x] Repo -- ldraney/paldocs
    • [x] User Story
    • [x] Context (updated with eager-load note)
    • [x] File Targets
    • [x] Acceptance Criteria (10 items, including new eager-load AC)
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    All required sections for Feature type are present and complete.

    Traceability

    • [x] story:project-pages label -- present on board item
    • [x] story note verified -- story-paldocs-project-pages exists in pal-e-docs
    • [x] arch:rails-views label -- present on board item
    • [x] arch note verified -- arch-rails-views exists in pal-e-docs
    • [x] Forgejo issue -- ldraney/paldocs#12, state: open

    Minor note: The project-paldocs user-stories section still reads "No user-story notes created yet" -- the story note exists but the project page text was not updated to reference it. This is a project-page maintenance item, not a ticket scope blocker.

    File Targets

    • [x] app/controllers/projects_controller.rb -- verified: exists (14 lines). Show action loads @project with includes(:notes, :repos). Context correctly identifies the missing :page_note eager-load. Agent has clear instructions.
    • [x] app/views/projects/show.html.erb -- verified: exists (64 lines). Currently renders notes list and repos. No block content rendered. Clear insertion point above the notes section.
    • [x] app/views/blocks/_block.html.erb -- new partial to create. Directory app/views/blocks/ does not exist yet (expected for new partial).
    • [x] app/assets/stylesheets/application.css -- verified: exists (677 lines). Single stylesheet with Gruvbox design tokens. Issue correctly targets this file for block content styling. Follows existing component pattern (comment headers with partial/stimulus references).

    All file targets verified. "Do not touch" files confirmed correct: app/models/block.rb (exists, has belongs_to :note), app/models/project.rb (exists, has belongs_to :page_note), no db/ changes needed.

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets within paldocs. Single-repo change.

    Dependencies

    • Item #1220 (done) -- "Project pages -- browse projects, view notes/repos/board link" -- predecessor that created the project show page. Complete.
    • Item #1344 (backlog) -- "feat: add Postgres trigger to bump projects.updated_at" -- shares story:project-pages but is independent (DB trigger). Not blocking.
    • Item #1342 (backlog) -- "feat: group kanban cards by phase within columns" -- shares arch:rails-views but is a different feature. Not blocking.
    • Item #1345 (in_progress) -- "spike: audit infra/pipeline gaps" -- unrelated. Not blocking.
    • No blocking dependencies. None needed.

    Acceptance Criteria

    10 acceptance criteria, all testable by an agent:

    • AC 1-7: Block-type rendering (heading, paragraph, list, table, code, mermaid) -- each verifiable by reading the generated partial and checking rendered output.
    • AC 8: Null safety for projects without page_note -- verifiable by visiting any project without a page_note.
    • AC 9: Existing sections remain below -- verifiable by inspecting view structure.
    • AC 10: No N+1 queries -- verifiable by checking controller includes and confirming eager-load.

    Test commands are real: bin/rails test, manual URL visits. No missing criteria.

    Blast Radius

    • New _block.html.erb partial is only rendered from project show page. No downstream consumers.
    • First block-rendering code in the Rails app. Future note show pages could reuse this partial.
    • This feature introduces the first html_safe usage in views -- currently zero instances exist. Constraints section appropriately warns about sanitization.
    • No risk to existing layout: new content section inserts above existing notes/repos sections.

    Decomposition Assessment

    4 file targets (2 existing, 2 new) in 1 repo. 10 acceptance criteria. The block partial covers multiple sub-types but follows a single case/when pattern in one file. Estimated agent work: 3-5 minutes. Within the 5-minute threshold. No decomposition needed.

    Recommendation

    No action needed. All previous findings resolved. Ticket is ready for implementation.

  • Verdict: APPROVED

    Re-review after refinement. Previous review (review-1342-2026-06-06) found 6 issues: wrong CSS path, missing kanban_controller.js file target, underspecified drag AC, and missing traceability notes. All 6 have been addressed in the updated issue body and pal-e-docs.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage
    • [x] Repo -- ldraney/paldocs
    • [x] User Story
    • [x] Context
    • [x] File Targets
    • [x] Acceptance Criteria
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    Traceability

    • [x] story:kanban label -- present on board item
    • [x] story note verified -- story-paldocs-kanban exists in pal-e-docs (note_type: doc, project: paldocs)
    • [ ] project page user-stories section NOT updated -- still says "No user-story notes created yet." The note exists but the project page does not reference it. Minor gap, does not block ticket. [SCOPE] Update project-paldocs user-stories section to reference story-paldocs-kanban.
    • [x] arch:rails-views label -- present on board item
    • [x] arch note verified -- arch-rails-views exists in pal-e-docs (note_type: doc, project: paldocs, sections: Conventions, Key Files)
    • [x] Forgejo issue -- ldraney/paldocs#11, state: open

    File Targets

    • [x] app/controllers/boards_controller.rb -- verified: exists, 9 lines. Currently groups items by column via flat position ordering. Correct target for adding phase grouping logic.
    • [x] app/views/boards/show.html.erb -- verified: exists, 30 lines. Iterates columns and renders cards in flat list. Correct target for phase group headers.
    • [x] app/views/board_items/_card.html.erb -- verified: exists, 45 lines. Already renders labels as badges. Correct target for phase badge styling.
    • [x] app/assets/stylesheets/application.css -- verified: exists, 676 lines. Kanban styles at lines 453-676 (confirmed: line 453 is .kanban {, line 676 closes the last media query). Issue correctly identifies the line range. Previously flagged as wrong path (kanban.css) -- now fixed.
    • [x] app/javascript/controllers/kanban_controller.js -- verified: exists, 199 lines. All 5 methods cited in the issue confirmed: _dropPosition (line 179), drop (line 45), cardClick (line 73), moveUp (line 124), moveDown (line 139). Issue correctly describes flat-list assumptions that will break with nested containers. Previously missing from File Targets -- now added with detailed method notes.

    Repo Placement

    OK -- issue filed on ldraney/paldocs, repo field says ldraney/paldocs, all 5 file targets are in this repo. Single-repo change.

    Dependencies

    • Item #1221 (done) -- "Interactive kanban board -- drag-and-drop columns with Turbo + Stimulus" -- the predecessor feature. Complete.
    • Item #1345 (in_progress) -- "spike: audit infra/pipeline gaps" -- independent, no conflict.
    • Item #1343 (backlog) -- "feat: render project page note content" -- shares arch:rails-views label but is independent work on a different view.
    • Item #1344 (backlog) -- "feat: add Postgres trigger to bump projects.updated_at" -- independent DB work.
    • No blocking dependencies. No conflicts with in-progress items.

    Acceptance Criteria

    7 acceptance criteria. All are manually testable. Improvements from previous review:

    • AC #5 now explicitly covers drag-and-drop across phase groups within the same column (previously missing).
    • AC #6 covers cross-column drag with phase group targeting.
    • AC #7 specifies ordering rule: "ordered by position of the phase board item within the column" (previously ambiguous).
    • All AC are verifiable via manual browser testing.

    Blast Radius

    Low. The kanban Stimulus controller is the only drag-and-drop controller in the app. No sibling services use it. CSS changes are in the single application.css file. The constraint "no schema changes" correctly limits blast radius to view/controller/JS layers. The main risk (breaking existing drag-and-drop) is acknowledged in the File Targets note for kanban_controller.js.

    Decomposition Assessment

    5 file targets in 1 repo, 7 acceptance criteria. The AC count (7) exceeds the >5 threshold. However, the criteria are tightly coupled -- all aspects of a single visual grouping feature across one controller, one view, one partial, one CSS file, and one JS controller. Decomposing would create artificial boundaries between inseparable concerns. At 5 story points and single-repo scope, this is feasible in one agent pass. No decomposition needed.

    Previous Review Findings -- Resolution Status

    • [x] [BODY] CSS path fixed: kanban.css corrected to application.css with line range (453-676)
    • [x] [BODY] kanban_controller.js added to File Targets with detailed method-level notes
    • [x] [BODY] Phase group ordering clarified in AC #7
    • [x] [BODY] Cross-phase-group drag AC added (AC #5 and #6)
    • [x] [SCOPE] story-paldocs-kanban note created
    • [x] [SCOPE] arch-rails-views note created

    Recommendations

    • [SCOPE] Update project-paldocs user-stories section to reference story-paldocs-kanban (minor -- does not block ticket advancement)

    No other action needed. Ticket is ready for development.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

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

    Traceability

    • [x] story:kanban label -- present on board item
    • [ ] story note MISSING -- project-paldocs user-stories section says "No user-story notes created yet." No story:kanban note exists. [SCOPE] Create user story entry for kanban on project-paldocs user-stories section.
    • [x] arch:rails-views label -- present on board item
    • [ ] arch note MISSING -- search for "arch-rails-views" returned no results. [SCOPE] Create architecture note arch-rails-views for component rails-views.
    • [x] Forgejo issue -- ldraney/paldocs#11, state: open

    File Targets

    • [x] app/controllers/boards_controller.rb -- verified: exists (367 bytes), groups items by column via flat position ordering. Correct target for adding phase grouping logic.
    • [x] app/views/boards/show.html.erb -- verified: exists, iterates columns and renders cards in flat list. Correct target for phase group headers.
    • [x] app/views/board_items/_card.html.erb -- verified: exists, already renders labels. Correct target for phase badge styling.
    • [ ] app/assets/stylesheets/kanban.css -- ISSUE: File does not exist. All kanban CSS is in app/assets/stylesheets/application.css (lines 453-676). [BODY] Fix file path: app/assets/stylesheets/kanban.css should be app/assets/stylesheets/application.css.
    • [ ] app/javascript/controllers/kanban_controller.js -- ISSUE: NOT listed as a file target but MUST be modified. The Stimulus controller (200 lines) handles drag-and-drop using flat .kanban-column-body children. Phase grouping introduces nested containers that will break: (1) _dropPosition() which queries .kanban-card:not(.is-dragging) within the body, (2) the drop handler's DOM insertion via body.children[position], (3) tap-to-move position calculation in cardClick, (4) arrow-based reordering in moveUp/moveDown. [BODY] Add app/javascript/controllers/kanban_controller.js to File Targets with note: "Update drag-and-drop logic to work across phase group boundaries."

    Repo Placement

    OK -- issue filed on ldraney/paldocs, repo field says ldraney/paldocs, all file targets are in this repo.

    Dependencies

    No blocking dependencies found on board-paldocs. Item #1221 (Interactive kanban board -- drag-and-drop) is in done and is the predecessor. No other in-progress items conflict. The constraint "Must not break existing drag-and-drop Stimulus controller" is correct but the issue underestimates the required changes to that controller.

    Acceptance Criteria

    5 acceptance criteria -- at the threshold. Criteria are testable via manual verification. However:

    • "Drag-and-drop still works within and across phase groups" is underspecified -- does not acknowledge that the Stimulus controller needs modification to support grouped containers.
    • "Phase groups maintain correct ordering" -- unclear what ordering means (alphabetical? position-based? creation-order?).
    • Missing AC: What happens when a card is dragged from one phase group to another within the same column? Does the phase label update?

    Blast Radius

    The kanban Stimulus controller is the only drag-and-drop controller in the app. No sibling services are affected. The CSS changes are in the single application.css file. The main risk is breaking existing drag-and-drop functionality if the Stimulus controller changes are not carefully scoped.

    Decomposition Assessment

    4 file targets listed (should be 5 with kanban_controller.js), 5 acceptance criteria, all in 1 repo. The Stimulus controller modification is non-trivial (nested container support for drag-and-drop). Estimated agent work is within 5 minutes if the file targets and AC are corrected. No decomposition needed, but the scope must be tightened first.

    Recommendations

    • [BODY] Fix file path: app/assets/stylesheets/kanban.css should be app/assets/stylesheets/application.css
    • [BODY] Add app/javascript/controllers/kanban_controller.js to File Targets with note: "Update drag-and-drop position calculation and DOM insertion to work across phase group boundaries"
    • [BODY] Clarify AC: "Phase groups maintain correct ordering" -- specify ordering rule (alphabetical, position-based, etc.)
    • [BODY] Add AC: specify behavior when dragging cards between phase groups within the same column
    • [SCOPE] Create user story entry for story:kanban on project-paldocs user-stories section
    • [SCOPE] Create architecture note arch-rails-views for component rails-views
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone
    • [x] Repo -- ldraney/paldocs
    • [x] User Story
    • [x] Context
    • [x] File Targets
    • [x] Acceptance Criteria
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    All required sections for Feature type are present.

    Traceability

    • [x] story:project-pages label -- present on board item
    • [ ] story note MISSING -- [SCOPE] The user-stories section on project-paldocs says "No user-story notes created yet." Create user story entry for "project-pages" on project-paldocs user-stories section.
    • [x] arch:rails-views label -- present on board item
    • [ ] arch note MISSING -- [SCOPE] No arch-rails-views note found in pal-e-docs. Create architecture note arch-rails-views for the Rails views component.
    • [x] Forgejo issue -- ldraney/paldocs#12, state: open

    File Targets

    • [x] app/controllers/projects_controller.rb -- verified: exists, show action loads @project with includes(:notes, :repos). Note: page_note is NOT eager-loaded; controller will need to add page_note to includes or use a separate query.
    • [x] app/views/projects/show.html.erb -- verified: exists, currently renders notes list and repos but no block content. Page note is not referenced anywhere in the view.
    • [x] app/views/blocks/_block.html.erb -- new partial to create (does not exist yet, correct)
    • [x] app/assets/stylesheets/blocks.css -- new stylesheet to create (does not exist yet, correct)
    • [x] app/models/block.rb -- verified: exists with belongs_to :note, correctly excluded from modification
    • [x] app/models/project.rb -- verified: belongs_to :page_note association exists, correctly excluded from modification
    • [x] app/models/note.rb -- verified: has_many :blocks association exists (not mentioned in issue but confirms feasibility)
    • [x] docs/data-model.md -- verified: exists, documents blocks table schema with block_type, content (json), anchor_id, position

    Repo Placement

    OK. Issue filed on ldraney/paldocs, all file targets are within paldocs. Single-repo change.

    Dependencies

    • Item #1220 (done) -- "Project pages -- browse projects, view notes/repos/board link" -- the foundational feature that created the project show page. This is the predecessor and is complete.
    • Item #1344 (backlog) -- "feat: add Postgres trigger to bump projects.updated_at" -- shares story:project-pages label but is independent (DB trigger, not view layer). Not blocking.
    • Item #1342 (backlog) -- "feat: group kanban cards by phase within columns" -- shares arch:rails-views label but is a different feature. Not blocking.
    • No blocking dependencies found. Dependencies are not documented in the issue but none are needed.

    Acceptance Criteria

    9 acceptance criteria, all testable by an agent. Test commands are real (bin/rails test, manual URL visits). However:

    • AC for heading blocks says "h2/h3/h4 based on content.level" -- the pal-e-docs block format uses numeric levels (2, 3, 4). Agent can verify by reading list_blocks output. Testable.
    • AC for code blocks references "content.code or content.text" -- the data model uses "content" JSON field. Agent should verify actual field names from real block data. Minor ambiguity but acceptable.
    • No missing criteria detected. The 9 AC are comprehensive for the scope.

    Blast Radius

    • The _block.html.erb partial is new and will only be rendered from the project show page. No downstream consumers.
    • No other views currently render blocks -- this is the first block-rendering code in the Rails app. Future features (e.g., note show pages) could reuse this partial.
    • The existing notes list and repos sections are below the new content per AC -- no risk of breaking existing layout if implemented correctly.
    • No html_safe or raw usage exists anywhere in current views -- this feature introduces the first use. The Constraints section appropriately warns about sanitization.

    Decomposition Assessment

    4 file targets (2 existing, 2 new) in 1 repo. 9 acceptance criteria. Estimated agent work: 3-5 minutes. The block partial has multiple sub-types (heading, paragraph, list, code, mermaid, table) but they are all in a single file with a case/when pattern. Within the 5-minute threshold. No decomposition needed.

    Recommendations

    • [BODY] Add note to File Targets: "The controller's show action currently does includes(:notes, :repos) but does NOT include :page_note. The agent should add :page_note to the includes call or load blocks via a separate query to avoid N+1."
    • [SCOPE] Create user story entry for "project-pages" on project-paldocs user-stories section.
    • [SCOPE] Create architecture note arch-rails-views for the Rails views component.
  • Verdict: READY

    Template Completeness

    • [x] Type -- "Spike"
    • [x] Lineage -- "Standalone -- discovered while comparing paldocs pipeline against landscaping-assistant."
    • [x] Repo -- ldraney/paldocs
    • [x] Question -- clearly framed: what are the gaps and what does the target architecture look like?
    • [x] What to Explore -- detailed list of 7 investigation areas with specific file paths and repos
    • [x] Success Criteria -- 5 concrete checkboxes covering doc creation, gap analysis, README link, implementation ticket, PR merge
    • [x] Time-box -- "1 session"
    • [x] Related -- links to paldocs project and issue #13

    All sections required by template-issue-spike are present and complete.

    Traceability

    • [x] story:infra label -- present on board item
    • [ ] story note MISSING -- No user story entries exist on project-paldocs yet. The user-stories section reads "No user-story notes created yet." However, this is foundational infrastructure work -- acceptable for a spike. [SCOPE] Create "infra" user story entry on project-paldocs user-stories section.
    • [x] arch:ci-cd label -- present on board item
    • [ ] arch note MISSING -- No arch-ci-cd note found in pal-e-docs. [SCOPE] Create architecture note arch-ci-cd for CI/CD component. However, this spike's deliverable is precisely to document this architecture -- the note's absence is expected and will be addressed by the spike output.
    • [x] Forgejo issue -- ldraney/paldocs#14, state: open

    File Targets

    Spike type -- no file targets required. The "What to Explore" section references files for investigation, not modification. Verified key files exist:

    • [x] .woodpecker.yaml -- verified: exists, contains test + build-and-push + update-kustomize-tag steps
    • [x] bin/docker-entrypoint -- verified: exists, currently skips db:prepare (as noted in issue)
    • [x] Confirmed: no docker-compose.yml exists (gap correctly identified in issue)
    • [x] docs/ directory exists with data-model.md and user-stories.md -- infrastructure.md does not yet exist (expected, it's the deliverable)
    • [x] README.md exists -- currently has no link to infrastructure docs (expected, link is a deliverable)

    Repo Placement

    OK. Issue is filed on ldraney/paldocs and the primary investigation + deliverable (docs PR) belongs in this repo. Cross-repo references (pal-e-deployments, pal-e-services, pal-e-platform, landscaping-assistant) are investigation targets only -- no changes needed in those repos for this spike.

    Dependencies

    • ldraney/paldocs#13 (feat: add Postgres trigger to bump projects.updated_at) -- open. Issue body notes #13 "depends on entrypoint fix from this work." This spike may produce an implementation ticket that #13 depends on, but the spike itself is not blocked.
    • No other board items on board-paldocs block this spike. All other items are in backlog or done.

    Acceptance Criteria

    5 success criteria, all verifiable by an agent:

    • [x] docs/infrastructure.md created with mermaid diagram -- verifiable via file existence + content check
    • [x] Gap analysis table included -- verifiable via content check
    • [x] README.md links to new doc -- verifiable via grep
    • [x] Implementation ticket created -- verifiable via Forgejo API
    • [x] PR merged -- verifiable via Forgejo API

    All criteria are concrete and machine-verifiable. No missing criteria detected.

    Blast Radius

    Low. This is a documentation spike. The direct output is a docs PR and a follow-up implementation ticket. No code changes to production systems. The implementation ticket (created as a deliverable) will have its own blast radius assessed at review time.

    The "known gaps" listed in the issue (no docker-compose, no rubocop, no test suite, archlinux:latest build image, external Harbor URL, skipped db:migrate) are confirmed by codebase inspection. The Gemfile has no rspec/minitest/rubocop gems. The entrypoint skips migrations. The pipeline uses archlinux:latest as the test image.

    Decomposition Assessment

    No decomposition needed. This is a spike with documentation-only output. No file targets to count. 5 success criteria (at the threshold but appropriate for a spike that produces a single docs PR). Estimated agent work well under 5 minutes -- the investigation is straightforward comparison work with a known deliverable format.

    Recommendation

    • [SCOPE] Create "infra" user story entry on project-paldocs user-stories section. Acceptable to defer -- foundational infrastructure work.
    • [SCOPE] Create architecture note arch-ci-cd for CI/CD component. Expected to be addressed by the spike output itself -- the spike's purpose is to document this architecture.

    Neither scope item blocks execution. The spike is well-formed, properly time-boxed, and has clear deliverables. Both missing notes are expected to be natural outputs of the spike work itself.

    No action needed to advance this ticket.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, discovery context provided
    • [x] Repo -- ldraney/paldocs
    • [x] User Story -- properly formatted As/I want/So that
    • [x] Context -- clear motivation, solution approach explained
    • [x] File Targets -- 3 files to modify/create, 1 exclusion documented
    • [x] Acceptance Criteria -- 8 items, well-scoped
    • [x] Test Expectations -- 3 manual checks + run command
    • [x] Constraints -- shared DB, NULL handling, plpgsql noted
    • [x] Checklist -- present
    • [x] Related -- project + related issue #14 linked

    Traceability

    • [x] story:project-pages label -- present on board item
    • [ ] story note MISSING -- project-paldocs user-stories section exists but has no entries ("No user-story notes created yet"). [SCOPE] Create user story entry for story:project-pages on project-paldocs user-stories section.
    • [x] arch:rails-db label -- present on board item
    • [ ] arch note MISSING -- search for "arch-rails-db" returned zero results. [SCOPE] Create architecture note arch-rails-db for the Rails database integration component.
    • [x] Forgejo issue -- ldraney/paldocs#13, state: open

    File Targets

    • [ ] db/migrate/YYYYMMDD_add_project_activity_trigger.rb -- ISSUE: The db/migrate/ directory does not exist. Zero migrations exist in this repo. The docs/data-model.md explicitly states: "No db/schema.rb or db/migrate/ in this repo" and "all DDL is managed by the FastAPI pal-e-docs service." Creating a Rails migration contradicts the established shared-DB architecture. The trigger DDL should be managed by the Python pal-e-docs layer (Alembic or raw SQL), not by a Rails migration.
    • [x] bin/docker-entrypoint -- verified exists (6 lines). Currently contains comment "Shared database -- Rails does not manage migrations. Skip db:prepare; tables already exist (managed by pal-e-docs FastAPI)." Adding db:migrate here contradicts the architecture. However, if the DDL ownership question is resolved in favor of Rails managing triggers, this change makes sense.
    • [x] docs/data-model.md -- verified exists (129 lines). Contains ER diagram and shared-DB documentation. Section about triggers would be appropriate here.
    • [x] app/controllers/projects_controller.rb -- verified: sort query is Project.order(updated_at: :desc) on line 3, correctly excluded from modification targets.

    Repo Placement

    Issue is filed on ldraney/paldocs, which is the Rails app. However, the core question is whether this trigger DDL belongs in the Rails repo or in the Python pal-e-docs service that currently owns all DDL. The issue assumes Rails ownership without addressing this architectural decision. The docs/data-model.md in the same repo explicitly says DDL is managed by FastAPI. This is an internal contradiction that must be resolved before implementation.

    Dependencies

    • Issue #14 (spike: audit infra/pipeline gaps) is referenced as a dependency for the entrypoint pattern decision. Issue #14 is open and in backlog on the same board (item #1345). The ticket says it "can proceed independently" but modifying the entrypoint without the spike's findings risks rework.
    • The Python pal-e-docs service (separate repo) owns all DDL. If the trigger is added via Rails migration, this breaks the DDL ownership contract. The Python service's Alembic/migration system is not accounted for.

    Acceptance Criteria

    ACs are well-structured and testable by an agent, with one exception: AC "Projects index sorts by actual activity after deploy" is vague -- it depends on deploy happening and data existing. The manual test expectations are reasonable. The bin/rails db:migrate run command is valid if the migration approach is approved.

    Missing AC: No criterion for verifying the trigger doesn't conflict with the Python layer's writes to the notes table. Should verify that Python INSERT/UPDATE on notes still works after trigger is added.

    Blast Radius

    • The Python pal-e-docs API performs heavy writes to the notes table (create, update, bulk operations via MCP). A Postgres trigger on notes INSERT/UPDATE will fire for every Python write. While the trigger is simple (bump parent project's updated_at), this is an invisible side effect for the Python layer.
    • Board items also reference projects (indirectly through board notes). Should the trigger also fire on board_item changes? The ticket only covers notes, but board activity is also "project activity."
    • The blocks table (child of notes) is also a signal of project activity. Not covered.

    Decomposition Assessment

    3 file targets in 1 repo, 8 acceptance criteria. The file count is fine, but the 8 ACs are at the threshold. More importantly, the architectural question (DDL ownership) must be resolved before implementation can proceed. No decomposition needed for the implementation itself -- this is a single-pass task once the approach is decided. However, the DDL ownership decision is a prerequisite that may change the file targets entirely.

    Recommendations

    • [SCOPE] Resolve DDL ownership: should this trigger be a Rails migration or managed via the Python pal-e-docs service? The current architecture (documented in docs/data-model.md) says all DDL is Python-managed. If Rails takes ownership of triggers, update docs/data-model.md to document the exception. If Python keeps ownership, the file targets change entirely (no Rails migration, no entrypoint change).
    • [BODY] Fix file target: db/migrate/YYYYMMDD_add_project_activity_trigger.rb -- db/migrate/ directory does not exist and the architecture forbids Rails migrations. Either justify the exception or move DDL to the Python layer.
    • [BODY] Add acceptance criterion: verify Python-layer writes to notes table still function correctly after trigger is installed.
    • [SCOPE] Create user story entry for story:project-pages on project-paldocs user-stories section.
    • [SCOPE] Create architecture note arch-rails-db for the Rails database integration component.
Architecture 7
  • Keycloak: paldocs

    OIDC authentication for paldocs, following the established pattern from landscaping-assistant (the reference implementation for Rails + Keycloak on this platform).

    Diagram

    sequenceDiagram
        participant User as User (Browser/iOS)
        participant Rails as paldocs (Rails 8)
        participant KC as Keycloak
        participant DB as PostgreSQL (CNPG)
    
        User->>Rails: GET /sprints (unauthenticated)
        Rails->>User: 302 Redirect to /auth/keycloak
        User->>KC: Authorization Code request (PKCE S256)
        KC->>User: Themed login page (paldocs CSS)
        User->>KC: Submit credentials
        KC->>User: 302 Redirect to /auth/keycloak/callback?code=...
        User->>Rails: GET /auth/keycloak/callback?code=...
        Rails->>KC: Exchange code for tokens (server-side, confidential client)
        KC->>Rails: ID token + access token + refresh token
        Rails->>Rails: Extract {username, email, roles} from ID token
        Rails->>DB: Find or create user session
        Rails->>User: 302 Redirect to /sprints (session cookie set)
    

    Components

    Component Purpose Notes
    Keycloak Server Identity provider (OIDC) quay.io/keycloak/keycloak:26.0.7, keycloak namespace, URL: https://keycloak.tail5b443a.ts.net. H2 dev-file DB with 2Gi PVC.
    Keycloak Realm (TBD) Auth boundary for paldocs users Each project gets its own realm (platform convention). Managed via keycloak_realms map in pal-e-services/terraform/k3s.tfvars.
    OIDC Client (TBD) paldocs client registration in Keycloak Confidential, Auth Code + PKCE (S256), realm roles mapper enabled, refresh tokens enabled. Managed via keycloak_clients map in k3s.tfvars, applied by keycloak.tf for_each.
    Login Theme CSS-only branded login page Three-repo pattern: source in paldocs repo (keycloak-theme/), ConfigMap in pal-e-platform (modules/keycloak/main.tf), login_theme on realm in pal-e-services. Extends base keycloak parent theme.
    OmniAuth (Rails) Server-side OIDC integration omniauth-openid-connect gem. Callback: /auth/keycloak/callback. Session stores user info from ID token.
    NetworkPolicy Network access from paldocs to keycloak namespace Keycloak has default-deny ingress. Currently allows: tailscale, westside-ai-assistant, pal-enterprises, landscaping-assistant, monitoring. Paldocs must be added.

    Key Decisions

    • Confidential client, not public: Rails is server-rendered. The client secret stays on the server and never reaches the browser. Matches pal-enterprises and landscaping-assistant patterns.
    • Auth Code + PKCE, not ROPC: Direct Access Grants (ROPC) is prohibited by OAuth 2.1 (RFC 9700). Landscaping-assistant tried ROPC and immediately reverted (PR #158). PKCE S256 is defense-in-depth even for confidential clients.
    • Refresh tokens enabled: Required for long-lived mobile sessions (turbo-ios). Users expect to stay logged in on their phones.
    • Realm roles in tokens: include_realm_roles_mapper = true puts roles at realm_access.roles in the ID/access token. Rails reads roles directly from the token without an extra Keycloak API call.
    • CSS-only theme: No custom FreeMarker templates, no Docker image rebuild. CSS overrides PatternFly defaults. Design tokens mirror the paldocs application.css.
    • Terraform-managed, not manual: Adding a client is a k3s.tfvars entry + tofu apply. The old SOP for manual admin console creation is superseded.
    • arch-infra -- Public domain affects Keycloak redirect URIs (paldocs.app/auth/keycloak/callback)
    • arch-k8s -- Deployment overlay needs KEYCLOAK_* env vars from k8s secret
    • arch-rails-db -- Shared database where user/session data lives
    • story-paldocs-ios-testflight -- Auth is on the critical path for iOS
    • paldocs #26 -- Keycloak TF client creation
    • paldocs #82 -- Login theme
    • pal-e-deployments #224 -- KEYCLOAK env vars in deployment overlay
    • pal-e-platform #303 -- Keycloak client tracking (platform-level)
    • arch-keycloak -- pal-enterprises Keycloak note (same platform pattern, different project)
    • landscaping-assistant/docs/keycloak-setup.md -- Reference: complete walkthrough of the same pattern
    • landscaping-assistant/docs/auth-strategy.md -- Reference: Auth Code + PKCE rationale
    • pal-e-services/terraform/keycloak.tf -- OIDC client resources (for_each on keycloak_clients)
  • Database: paldocs

    How paldocs (Rails 8) and pal-e-api (FastAPI) share the same CNPG PostgreSQL cluster, with separate migration ownership.

    Diagram

    graph TB
        subgraph "paldocs namespace"
            RAILS["paldocs PodRails 8 + ActiveRecord"]
        end
    
        subgraph "pal-e-docs namespace"
            FASTAPI["pal-e-api PodFastAPI + SQLAlchemy"]
        end
    
        subgraph "postgres namespace"
            CNPG["pal-e-postgres (CNPG)PostgreSQL 17.4"]
            DB["paledocs database"]
            CNPG --- DB
        end
    
        subgraph "MinIO"
            WAL["s3://postgres-wal/Barman backups"]
        end
    
        RAILS -->|"PALDOCS_DATABASE_URLActiveRecord migrations"| DB
        FASTAPI -->|"DATABASE_URLAlembic migrations"| DB
        CNPG -->|"gzip WAL + daily backup"| WAL
    

    Components

    Component Purpose Notes
    CNPG Cluster (pal-e-postgres) Managed PostgreSQL 17.4 Defined in pal-e-services/terraform/cnpg.tf. Single instance, 5Gi local-path, postgres namespace. Superuser access enabled.
    paledocs database Shared database for both apps Created by CNPG bootstrap.initdb (only runs on cluster creation). Owner: paledocs user. Credentials in paledocs-db-credentials k8s secret.
    Alembic (pal-e-api) Migrations for original schema Owns: notes, blocks, tags, projects, links, boards, revisions, embeddings (24 migrations). SQLAlchemy 2.0 models.
    ActiveRecord (paldocs) Migrations for Rails-specific tables Owns: sprints, Solid Queue tables, any new Rails models. Schema bootstrapped via pg_dump to structure.sql.
    Barman backups WAL archiving + scheduled backups Continuous WAL to MinIO (s3://postgres-wal/), gzip compressed. Daily full backup at 02:00 UTC. 7-day retention.
    NetworkPolicy Postgres namespace ingress control Paldocs is already in the postgres allowlist alongside pal-e-docs, pal-enterprises, landscaping-assistant, palinks, and others.

    Key Decisions

    • Shared database, not separate databases: Paldocs reads and writes the same notes/blocks/boards tables as pal-e-api. This avoids data sync between two databases and lets both apps operate on the same source of truth.
    • Schema ownership transfer via pg_dump: When Rails was introduced, the existing schema was exported via pg_dump, loaded as Rails' db/structure.sql, and Rails' first migration was a no-op stamp. This gave Rails a complete picture without re-running Alembic migrations.
    • Alembic owns pal-e-api columns: Schema changes touching notes, blocks, boards, or other pal-e-api models must go through Alembic in the pal-e-api repo. Paldocs must not create Rails migrations for these tables.
    • Rails owns its own tables: New tables used only by paldocs (sprints, Solid Queue, etc.) get Rails migrations in the paldocs repo.
    • Cross-cutting changes require ordered deploys: If a change touches both codebases, the Alembic migration deploys first (pal-e-api), then the Rails migration follows (paldocs). Order matters.
    • Not in service_databases map: Paldocs uses the CNPG bootstrap database (paledocs), not a dynamically provisioned database from databases.tf. If paldocs needs additional databases (Solid Queue, Action Cable), they would be added to service_databases.
    • arch-k8s -- Deployment overlay where DATABASE_URL is wired
    • arch-keycloak-paldocs -- Auth tables may require cross-cutting schema work
    • paldocs #27 -- Visibility column (cross-cutting: Alembic migration in pal-e-api, not a Rails migration)
    • pal-e-services/terraform/cnpg.tf -- CNPG cluster definition
    • pal-e-services/terraform/databases.tf -- Per-service database provisioning (for_each on service_databases)
    • pal-e-platform/terraform/network-policies.tf -- Postgres namespace ingress rules
  • Kubernetes: paldocs

    How paldocs is deployed on k3s. Covers the three-repo model, Kustomize overlays, ArgoCD GitOps, and network policies.

    Diagram

    graph TB
        subgraph "Forgejo"
            REPO_APP["ldraney/paldocs(app source)"]
            REPO_DEPLOY["ldraney/pal-e-deployments(Kustomize overlays)"]
        end
    
        subgraph "CI / Registry"
            WP["Woodpecker CIlint, test, build"]
            HARBOR["Harbor Registrypaldocs/app:SHA"]
        end
    
        subgraph "k3s — argocd namespace"
            IMGUP["ArgoCD Image Updaterdetects new tag"]
            ARGO["ArgoCD Applicationsource: overlays/paldocs/prod"]
        end
    
        subgraph "k3s — paldocs namespace"
            DEPLOY["Deployment: paldocsRails/Puma :3000"]
            SVC["Service: paldocsport 3000"]
            SMON["ServiceMonitor"]
        end
    
        subgraph "k3s — postgres namespace"
            CNPG["pal-e-postgresCNPG ClusterPostgreSQL 17.4"]
        end
    
        REPO_APP -->|"git push"| WP
        WP -->|"push image"| HARBOR
        HARBOR --> IMGUP
        IMGUP -->|"write tag to kustomization"| REPO_DEPLOY
        REPO_DEPLOY --> ARGO
        ARGO -->|"sync"| DEPLOY
        DEPLOY --> SVC
        DEPLOY --> SMON
        DEPLOY -->|"DATABASE_URL"| CNPG
    

    Components

    Component Purpose Notes
    pal-e-platform (Terraform) Cluster infrastructure: CNPG operator, network policies, namespaces (platform-level) Modules: database, networking, keycloak, monitoring, etc.
    pal-e-services (Terraform) Per-service automation via var.services for_each Creates: Harbor project, robot accounts (CI push + pull), k8s namespace, ArgoCD app, Tailscale funnel. Paldocs entry: port=3000, funnel=true, source_path=overlays/paldocs/prod.
    pal-e-deployments (Kustomize) App-level manifests: resource limits, env vars, probes, image tags Base: bases/standard/ (deployment, service, servicemonitor). Overlay: overlays/paldocs/prod/.
    ArgoCD + Image Updater GitOps continuous deployment Image Updater watches Harbor for tags matching ^[0-9a-f]{7,40}$ (git SHAs), writes new tag to kustomization. ArgoCD syncs with prune + self-heal.
    paldocs Deployment Rails 8 / Puma container Port 3000, security context: runAsNonRoot (UID 1000), drop ALL caps, RuntimeDefault seccomp. Resources: 50m/128Mi request, 512Mi limit.
    paldocs Service k8s service routing Port 3000 to 3000. No namespace in kustomization -- ArgoCD sets it.
    CNPG Cluster PostgreSQL 17.4 managed by CloudNativePG operator Cluster name: pal-e-postgres, namespace: postgres. Single instance, 5Gi local-path, Barman backups to MinIO.

    Key Decisions

    • No namespace in Kustomize overlays: ArgoCD controls namespace placement via the Application resource's destination.namespace. Hardcoding namespace in YAML would conflict.
    • Standard base with JSON patches: The paldocs overlay patches the generic bases/standard/ base using strategic merge and JSON patches. App name, port, and image are overridden; security context and probe patterns are inherited.
    • Default-deny network policies: Every namespace has a default-deny-ingress NetworkPolicy (managed in pal-e-platform/terraform/network-policies.tf). Paldocs namespace is already in the postgres allowlist. Must be added to keycloak allowlist for auth.
    • Env vars from k8s secrets: SECRET_KEY_BASE and PALDOCS_DATABASE_URL come from paldocs-secrets k8s secret. Keycloak env vars (KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET) must be added.
    • Health probes on /up: Rails convention endpoint. Liveness: 30s initial delay, 30s period. Readiness: 5s initial delay, 10s period.
    • arch-infra -- How public traffic reaches the paldocs service
    • arch-rails-db -- Shared CNPG cluster and dual-migration pattern
    • arch-keycloak-paldocs -- Auth env vars that must be added to the deployment overlay
    • pal-e-deployments #224 -- Add KEYCLOAK env vars to paldocs overlay
    • pal-e-deployments/overlays/paldocs/prod/ -- Current overlay (deployment-patch.yaml, kustomization.yaml)
    • pal-e-deployments/bases/standard/ -- Shared Kustomize base
  • Infrastructure: paldocs

    How public traffic reaches paldocs on k3s. Covers the Hetzner edge proxy, DNS via godaddy-tofu, and internal Tailscale funnels.

    Diagram

    graph TB
        subgraph "Public Internet"
            BROWSER["Browser / iOS App"]
            DNS["GoDaddy DNSpaldocs.app → A record"]
        end
    
        subgraph "Hetzner VPS (CAX11, Ashburn)"
            CADDY["CaddyTLS terminationreverse proxy"]
            TS_EDGE["Tailscale Agenttag:edge"]
        end
    
        subgraph "k3s Cluster"
            TS_FUNNEL["Tailscale Funnelpaldocs.tail5b443a.ts.net"]
            SVC["k8s Servicepaldocs:3000"]
            POD["paldocs PodRails/Puma"]
        end
    
        BROWSER -->|"HTTPS"| DNS
        DNS -->|"A record"| CADDY
        CADDY -->|"Tailscale mesh"| TS_EDGE
        TS_EDGE -->|"WireGuard tunnel"| TS_FUNNEL
        TS_FUNNEL --> SVC
        SVC --> POD
    
        BROWSER -->|"internal (Tailscale)"| TS_FUNNEL
    

    Components

    Component Purpose Notes
    GoDaddy DNS Maps public domains to Hetzner VPS IP Managed via godaddy-tofu provider in pal-e-platform/terraform/dns.tf. TTL 600s. Currently serves palinks.app, landscaping-assistant.app, westsidekingsandqueens.com.
    Hetzner VPS Edge reverse proxy for public-facing services CAX11 (Debian 12). Managed by pal-e-platform/terraform/modules/hetzner-edge/. Firewall allows TCP 22/80/443. Cloud-init installs Tailscale + Caddy.
    Caddy TLS termination + reverse proxy Automatic HTTPS via Let's Encrypt. Each domain gets a Caddyfile block proxying to the Tailscale hostname (e.g., paldocs.tail5b443a.ts.net:3000).
    Tailscale Agent (edge) Connects VPS to the tailnet mesh Hostname: edge-proxy. Ephemeral pre-authorized key with tag:edge.
    Tailscale Funnel Internal access to paldocs via tailnet Created by services.tf when funnel = true. URL: https://paldocs.tail5b443a.ts.net.
    paldocs Pod Rails 8 / Puma on port 3000 k8s Service paldocs routes to container port 3000.

    Key Decisions

    • Hetzner edge instead of Cloudflare/ALB: A simple VPS running Caddy + Tailscale avoids cloud vendor lock-in. Caddy's automatic HTTPS eliminates cert management. Total cost: ~$4/month for the CAX11.
    • DNS via Terraform (godaddy-tofu), not console clicks: All A records are IaC. Adding paldocs.app is a single godaddy_dns_record resource pointing at module.hetzner_edge.server_ipv4.
    • Dual access paths: Public users hit paldocs.app (via edge proxy). Internal users hit paldocs.tail5b443a.ts.net (direct Tailscale funnel). Both reach the same pod.
    • paldocs.app already owned: Domain is purchased. Only the A record + Caddy block need to be created.
    • arch-k8s -- Kubernetes deployment patterns for paldocs
    • arch-keycloak-paldocs -- Keycloak integration (public domain affects redirect URIs)
    • story-paldocs-ios-testflight -- iOS app requires a public domain (no Tailscale on App Store)
    • pal-e-platform #490 -- DNS + Caddy setup for paldocs.app
    • landscaping-assistant/docs/networking.md -- Reference: same edge proxy pattern already working
  • Rails Views: paldocs

    Diagram

    graph TB
        subgraph Browser
            HTML[ERB Templates]
            Turbo[Turbo Frames/Streams]
            Stim[Stimulus Controllers]
        end
        subgraph Rails
            Controllers[View Controllers]
            Models[ActiveRecord Models]
        end
        HTML --> Turbo
        Turbo --> Controllers
        Stim --> Controllers
        Controllers --> Models
    

    Components

    Component Purpose Notes
    ERB templates Server-rendered HTML Sanitized HTML rendering for note content
    Turbo Frames Partial page updates Section navigation, inline editing
    Turbo Streams Live updates Board item moves, real-time collaboration
    Stimulus controllers Client-side behavior Drag-and-drop kanban, search typeahead
    CSS (mobile-first) Responsive layout No JS framework, no Tailwind — plain CSS

    Key Decisions

    • Hotwire over React/Vue — stays within Rails ecosystem, no JS build pipeline
    • Mobile-first CSS — paldocs must work on phone for quick lookups
    • Sanitized HTML rendering — note content is stored as HTML, rendered with Rails sanitizer to prevent XSS
    • Stimulus for kanban — drag-and-drop board interactions without a full JS framework
    • project-paldocs — parent project page
    • arch-rails-api — API controllers share models with view controllers
    • story-paldocs-sprint-orchestration — sprint planning UI story
  • Rails Jobs: paldocs

    Diagram

    sequenceDiagram
        participant Model as Block (ActiveRecord)
        participant Queue as Solid Queue
        participant Job as EmbedBlockJob
        participant Ollama as Ollama API
        participant DB as PostgreSQL
        Model->>Queue: after_save enqueue
        Queue->>Job: dequeue and execute
        Job->>Ollama: POST /api/embeddings (qwen3-embedding:4b)
        Ollama-->>Job: 2560-dim vector
        Job->>DB: UPDATE blocks SET embedding = vector
    

    Components

    Component Purpose Notes
    EmbedBlockJob Primary embedding job Triggered by after_save on Block model
    Solid Queue Job backend Rails 8 default, replaces Sidekiq/Redis
    Ollama HTTP API Embedding model host qwen3-embedding:4b, 2560 dimensions
    neighbor gem Vector column writes Writes to blocks.embedding column

    Key Decisions

    • Solid Queue over Sidekiq — eliminates Redis dependency, aligns with Rails 8 defaults
    • after_save callback — every block save triggers re-embedding, keeping vectors fresh
    • Mermaid block skipping — blocks containing only mermaid diagrams are not embedded (no semantic value)
    • Retry with exponential backoff — handles Ollama timeouts gracefully
    • project-paldocs — parent project page
    • arch-rails-db — schema where vectors are stored
    • arch-infra — k8s deployment where jobs run
    • story-paldocs-consolidation — driving user story
  • Rails API: paldocs

    Diagram

    sequenceDiagram
        participant MCP as pal-e-mcp
        participant API as Rails /api/
        participant DB as PostgreSQL
        MCP->>API: GET /api/notes/:slug
        API->>DB: SELECT * FROM notes WHERE slug = ?
        DB-->>API: note row
        API-->>MCP: JSON response
        MCP->>API: POST /api/search
        API->>DB: vector cosine query via neighbor
        DB-->>API: ranked results
        API-->>MCP: JSON results
    

    Components

    Component Purpose Notes
    Api::NotesController Notes CRUD GET/POST/PUT/DELETE /api/notes
    Api::BoardsController Boards and items GET/POST/PUT /api/boards, /api/board_items
    Api::ProjectsController Project listing and detail GET /api/projects
    Api::SearchController Keyword + semantic search POST /api/search with vector fallback
    Api::BlocksController Block CRUD Nested under notes
    Api::TagsController Tag listing GET /api/tags

    Key Decisions

    • Namespaced under /api/ — keeps MCP-facing endpoints separate from HTML views
    • Endpoint parity with pal-e-docs FastAPI — MCP server requires no changes during migration
    • JSON-only responses on /api/ — no HTML rendering in API controllers
    • Semantic search endpoint combines keyword and vector search in a single call
    • project-paldocs — parent project page
    • arch-rails-db — schema this API queries
    • arch-rails-views — HTML views that share models with the API
    • story-paldocs-consolidation — driving user story
User Story 3
  • Ship Paldocs to App Store via TestFlight story-paldocs-ios-testflight

    story: Ship Paldocs to App Store via TestFlight

    Role

    Sprint Manager (Lucas)

    Key

    ios-testflight

    Want

    As a sprint manager, I want to access paldocs from my iPhone via a native App Store install

    So That

    So that I can manage sprints, review boards, and check project status without opening a browser

    Acceptance Criteria

    • paldocs.app resolves in a browser and shows the Rails app (authenticated via Keycloak)
    • Keycloak login page renders with a paldocs-branded CSS theme
    • turbo-ios app loads paldocs.app in a native WebView, handles the Keycloak login flow, and navigates between pages
    • TestFlight build installs on iPhone and functions identically to the web app
    • Sprint board is viewable and interactive from the iOS app
    • Most Rails changes ship to the iOS app on next launch without requiring an App Store rebuild

    Success Metric

    TestFlight build installs and loads the sprint board within 5 seconds of app launch. Auth persists across app restarts (refresh token session). No App Store review needed for server-side changes.

    • arch-infra -- Public domain (paldocs.app) is required; App Store apps cannot require Tailscale
    • arch-keycloak -- OIDC client with refresh tokens enabled for long-lived mobile sessions
    • arch-k8s -- Deployment overlay and CI/CD pipeline that serves the iOS app
    • arch-rails-db -- Database backing the sprint board data
    • pal-e-platform #490 -- DNS + Caddy for paldocs.app (sprint 3)
    • paldocs #26 -- Keycloak TF client creation (sprint 3)
    • paldocs #82 -- Login theme (sprint 3)
    • pal-e-deployments #224 -- KEYCLOAK env vars in deployment overlay (sprint 3)
    • paldocs #83 -- turbo-ios native shell (sprint 4, 8pt)
    • paldocs #84 -- Fastlane + TestFlight pipeline (sprint 4, 3pt)
    • paldocs #46 -- iOS App Store epic tracker
    • landscaping-assistant/docs/pipeline.md -- Reference: complete pipeline diagram (web + iOS)
    • landscaping-assistant/docs/keycloak-setup.md -- Reference: Keycloak client creation walkthrough
    • landscaping-assistant/docs/ios-dev-builds.md -- Reference: turbo-ios project setup and Fastlane config

    Critical Path

    Four dependencies in order:

    1. Domain (paldocs.app): DNS A record + Caddy reverse proxy on Hetzner edge VPS. Without a public URL, the iOS app has no server to connect to.
    2. Auth (Keycloak OIDC): Confidential client with Auth Code + PKCE (S256). OmniAuth in Rails. Refresh tokens for persistent mobile sessions. CSS login theme.
    3. turbo-ios shell: Xcode project wrapping paldocs.app in a Turbo Native WebView. Path configuration JSON for native vs web presentation. Cookie-based session via Rails session cookie.
    4. Fastlane pipeline: ssh macbook "cd ~/paldocs-ios && fastlane beta" builds, signs, and uploads to TestFlight. Requires $99/year Apple Developer enrollment. Builds last 90 days.

    Update Lifecycle

    Most changes ship through the Rails deploy pipeline (git push to Woodpecker CI to Harbor to ArgoCD) and are picked up by the iOS app on next launch -- no App Store review. The iOS shell only needs a rebuild when native capabilities change (push notifications, camera, app icon).

    Sprint Mapping

    Sprint Items
    Sprint 3 Domain + DNS (#490), Keycloak client (#26), login theme (#82), deployment env vars (#224)
    Sprint 4 turbo-ios project (#83, 8pt), Fastlane pipeline (#84, 3pt)
  • story: Sprint Orchestration — cross-project sprint planning UI

    Role

    Platform Owner (Lucas)

    Key

    sprint-orchestration

    Want

    As Lucas, I want a sprint planning interface in paldocs where I can select active projects, see their boards side-by-side, and organize tickets into parallel sprint batches

    So That

    So that sprint planning happens in a real UI instead of ad-hoc MCP calls

    Acceptance Criteria

    • UI shows all active projects with their boards
    • Boards can be viewed side-by-side for cross-project planning
    • Tickets can be assigned to sprints — each sprint is a flat parallel batch with no internal dependencies
    • Sprint view shows point totals across projects and highlights any dependency violations
    • Sprint plan can be saved and shared as a snapshot

    Success Metric

    Sprint planning for 3+ projects can be completed entirely within the paldocs UI without falling back to MCP calls.

    • arch-rails-views — ERB templates and Hotwire patterns for the planning UI
    • arch-rails-api — API endpoints for boards and items
    • arch-rails-db — sprint schema (sprints, sprint_projects tables)
    • project-paldocs — parent project page
    • board-paldocs — project board
  • story: Consolidation — absorb pal-e-docs into Rails monolith

    Role

    Platform Owner (Lucas)

    Key

    consolidation

    Want

    As the platform owner, I want paldocs (Rails 8) to absorb pal-e-docs (FastAPI) — owning the database schema, API endpoints, and embedding pipeline

    So That

    So that the knowledge layer runs as a single Rails monolith instead of two apps sharing a database

    Acceptance Criteria

    • Rails owns the paledocs PostgreSQL schema via ActiveRecord migrations (structure.sql format)
    • All pal-e-docs REST endpoints are served by Rails API controllers under /api/
    • Embedding pipeline runs as Solid Queue background jobs instead of Python workers
    • pal-e-mcp (or successor) calls Rails endpoints instead of FastAPI
    • pal-e-docs (FastAPI) can be shut down with no loss of functionality

    Success Metric

    pal-e-docs FastAPI service is decommissioned; all MCP tools and UI features operate against the Rails monolith with no regressions.

    • arch-rails-db — schema ownership and pgvector
    • arch-rails-api — MCP-compatible REST surface
    • arch-rails-jobs — Solid Queue embedding pipeline
    • project-paldocs — parent project page
    • board-paldocs — project board
Project Page 1
  • Project: paldocs project-paldocs

    Vision

    Human-facing frontend for the pal-e-docs platform. Rails 8 + Hotwire app that gives humans browser access to the same Postgres database that Claude agents access via MCP/SDK. Project pages, interactive kanban boards, and eventually full CRUD parity with the agent layer.

    User Stories

    Architecture

    • arch-rails-views — ERB template conventions, sanitized HTML rendering, project page structure
    • arch-rails-hotwire — Hotwire/Stimulus patterns (Turbo Frames, progressive enhancement, importmap registration)
    • arch-rails-db — schema ownership (Rails owns migrations via structure.sql), pgvector via neighbor gem
    • arch-rails-api — JSON API controllers serving the MCP-compatible REST surface
    • arch-rails-jobs — Solid Queue background jobs (embedding pipeline, async operations)
    • arch-keycloak-paldocs — Keycloak integration pattern for auth and visibility (pending spike #23)
    • arch-mcp — MCP server transition: pal-e-mcp → new paldocs MCP server backed by Rails API
    • arch-infra — k8s deployment, CI/CD pipeline, Harbor images, ArgoCD sync

    Key decisions:

    • Rails 8 + Hotwire — Turbo frames/streams + Stimulus for interactivity without a JS framework
    • Shared Postgres — connects to paledocs database at pal-e-postgres-rw.postgres.svc.cluster.local:5432 (CloudNativePG)
    • Rails owns schema — all migrations via ActiveRecord (structure.sql format), pgvector via neighbor gem. Replaces Alembic (decided 2026-06-22).
    • Absorbs pal-e-docs — Rails serves the API, runs the embedding pipeline (Solid Queue + Ollama), and provides the UI. FastAPI is deprecated.
    • CI — Woodpecker pipeline builds Docker image and pushes to Harbor

    Board

    board-paldocs

    Status

    As of 2026-06-23:

    • Prod (paldocs.tail5b443a.ts.net) — UP
    • CI pipeline fixed — PR #73 merged. Stripped Bundler 4.x checksums from Gemfile.lock, downgraded BUNDLED WITH to 2.6.9 to match CI image. Follow-up ticket needed to upgrade base image to Bundler 4.x.
    • Sprint 1 status: 4/5 tickets done (#49, #52, #57, #67). #50 (sprint schema) merged but validation blocked by CI — re-validate now that #73 is merged.
    • Schema ownership transferred — PR #60 merged. Rails is sole migration authority.
    • Consolidation sprint:1 re-cut (2026-06-23): Original 6-wave waterfall re-cut into 4 true parallel sprints per sprint philosophy. Sprint:1 = 4 parallel tickets (16pts).
    • Collapsible accordion sections with updated_at sort shipped (PR #32)
    • CI schema for shared pal-e-docs tables shipped (PR #34)
    • CI/CD pipeline shipped (PR #21) — rubocop, rspec, docker-compose, internal Harbor, CNPG-aligned Postgres 17
    • Kanban phase grouping shipped (PR #19)
    • Activity trigger (PR #17) and page note rendering (PR #18) shipped

    Milestones

    • 2026-06-23 — Sprint:1 nearly complete: 4/5 done, CI fix merged (PR #73). Sprint re-cut from 6-wave waterfall to 4 parallel sprints.
    • 2026-06-22 — Schema ownership transfer merged (PR #60). Consolidation sprint planned.
    • 2026-06-07 — Kanban phase grouping (PR #19), activity trigger (PR #17), page note rendering (PR #18), worktree convention (#20), and CI/CD infrastructure (PR #21) all shipped. Board cleared.
    • 2026-06-06 — Data model docs (PR #10) and infra spike (PR #16) merged. Feature tickets #11-#13 scoped and reviewed.
    • 2026-05-19 — Initial scaffold, project pages, and kanban board shipped

    Repos

    Repo Platform Role Status
    ldraney/paldocs Forgejo Rails frontend Active (private)

    Note: repo was renamed from pal-e-docs-rails to paldocs. Old Forgejo issue URLs reference the prior name.

Board 1