Prediction Assistant

prediction-assistant forgejo

Notes

Doc 2
  • ISS Rails Architecture

    Framework

    Ruby on Rails 8 — full-stack monolith serving both the public landing page and the authenticated client management app. The same codebase powers the marketing site at intelligentstaffingsystems.ai and the iOS app via Turbo Native.

    Asset Pipeline

    Propshaft — Rails 8 default asset pipeline. No Sprockets, no Node build step. CSS is served from app/assets/stylesheets/. JavaScript uses import maps.

    Mobile Delivery

    Turbo Native — the iOS app wraps the Rails backend in a native shell. Interface changes deploy by pushing to Rails; no App Store resubmission required for UI updates. The companion iOS repo consumes the same endpoints and views.

    Authentication

    Keycloak OIDC — all authentication is handled by a self-hosted Keycloak instance. The Rails app is an OIDC relying party. Three roles are enforced: prospect, client, and admin. Session management flows through Keycloak; the app never stores passwords.

    Testing

    Minitest — endpoint-first controller tests with no system/browser tests. Keycloak sessions are stubbed per role in the test helper. Tests run inside Docker via docker compose run web rails test. CI gates PRs on the full test suite.

    Development Environment

    Docker-based — the web image is pulled from Harbor (no local Dockerfile build). Local dev runs via docker compose up on port 9999. Production runs on port 3000 per the platform port convention.

    Key Directories

    • app/controllers/ — request handling; auth gating via before_action
    • app/views/ — ERB templates; tab-based navigation for the authenticated app
    • app/assets/stylesheets/ — Propshaft CSS (forms, pages, navigation)
    • config/routes.rb — route definitions; landing page, leads, dashboard, messaging
    • test/ — Minitest suite; controller tests stub Keycloak sessions
  • Ticket

    #1 Rails app scaffold with Kalshi API client (5pt, Sprint 1)

    Shipped: Rails 8 scaffold with KalshiClient service using RSA-PSS authentication, rate limiting, and market/portfolio/order endpoints.

    Environment

    Local development — this is a scaffold ticket. Production deployment is Sprint 2 scope (Kustomize #9, CI/CD #11). Validation covers code correctness, test suite, and app boot.

    Checks

    # Criterion How to Verify Result Evidence
    1 Rails app structure exists Check for Gemfile, config/application.rb, app/ directories PASS Standard Rails 8 structure: controllers, jobs, models, services directories present
    2 App boots cleanly bin/rails runner "puts 'OK'" PASS Prints OK with no errors
    3 Test suite passes bin/rails test PASS 20 tests, 50 assertions, 0 failures, 0 errors
    4 KalshiClient RSA-PSS auth Test round-trip RSA-PSS signature verify PASS Tests cover signature generation + verification, auth headers (KALSHI-ACCESS-KEY, KALSHI-ACCESS-TIMESTAMP, KALSHI-ACCESS-SIGNATURE), credential validation raises AuthenticationError
    5 API endpoint coverage Review KalshiClient methods PASS Markets (markets, market, orderbook), portfolio (balance), orders (create_order), exchange status. Generic get/post for extensibility.
    6 Rate limiting Test rate limit enforcement PASS Sliding-window rate limiting: 20 read/sec, 10 write/sec per Kalshi Basic tier
    7 Error handling Test 401/429/5xx responses PASS Tests cover authentication errors, rate limit responses, and server errors

    Verdict

    PASS — all 7 checks green. Scaffold is complete and functional. Production deployment validation will occur under Sprint 2 tickets (#9 Kustomize, #11 CI/CD).

    Discovered Issues

    None. The events endpoint is not a dedicated method but accessible via generic get — adequate for scaffold scope.

Review 66
  • Verdict: APPROVED

    Re-review after refinements requested by review-1812-2026-07-05 (NEEDS_REFINEMENT). All 7 previous findings resolved.

    Previous Findings Resolution

    • [x] Missing Lineage section -- NOW ADDED with upstream (pal-e-services#173 tofu apply) and downstream (#93 verify site live)
    • [x] Missing Acceptance Criteria -- NOW ADDED with 6 verifiable conditions
    • [x] Keycloak env var placement unspecified -- NOW CLARIFIED: ConfigMap for KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID; Secret for KEYCLOAK_CLIENT_SECRET
    • [x] redirect-middleware.yaml unclear -- NOW ANNOTATED as conditional ("only if needed; Tailscale funnels may handle HTTPS redirect natively")
    • [x] Undocumented tofu apply dependency -- NOW IN LINEAGE as upstream blocker
    • [x] [SCOPE] Create arch-k8s-deploy note -- NOW EXISTS in pal-e-docs (comprehensive k8s deployment architecture)
    • [x] [SCOPE] Create arch-keycloak note -- NOW EXISTS in pal-e-docs (Keycloak OIDC architecture)

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- present (upstream: pal-e-services#173, downstream: #93, sprint: 12)
    • [x] Repo -- prediction-assistant
    • [x] User Story -- present
    • [x] Context -- present, includes Keycloak env var placement and ingress clarification
    • [x] File Targets -- 7 targets (3 new, 4 existing)
    • [x] Feature Flag -- None (acceptable for infra work)
    • [x] Acceptance Criteria -- 6 items, all verifiable
    • [x] Test Expectations -- 3 items
    • [x] Constraints -- present
    • [x] Checklist -- present (Code complete checked)
    • [x] Related -- present

    Traceability

    • [x] story:platform-setup label -- Platform Setup
    • [x] story note verified -- found in project-prediction-assistant user-stories section (key: platform-setup, role: Developer)
    • [x] arch:k8s-deploy label -- Kubernetes Deployment
    • [x] arch note verified -- arch-k8s-deploy exists in pal-e-docs (created 2026-07-03, comprehensive with diagram, directory structure, components)
    • [x] arch:keycloak label -- Keycloak OIDC
    • [x] arch note verified -- arch-keycloak exists in pal-e-docs (client pattern, config location, Terraform-managed)
    • [x] Forgejo issue -- ldraney/prediction-assistant#105, closed (consistent with validation column -- implementation merged)

    File Targets

    • [x] k8s/overlays/prod/ingress.yaml (new) -- verified: does not exist, correctly marked as new
    • [x] k8s/overlays/prod/cluster-issuer.yaml (new) -- verified: does not exist, correctly marked as new
    • [x] k8s/overlays/prod/redirect-middleware.yaml (new, conditional) -- verified: does not exist. Now annotated as conditional on Tailscale funnel behavior. Previous concern RESOLVED.
    • [x] k8s/overlays/prod/kustomization.yaml -- verified: exists (73 lines, has image pin, patches, labels). New resources will need adding.
    • [x] k8s/base/configmap.yaml -- verified: exists with RAILS_ENV, RAILS_LOG_TO_STDOUT, RAILS_SERVE_STATIC_FILES, RAILS_MAX_THREADS, PORT. No Keycloak vars present (confirms gap).
    • [x] k8s/base/deployment.yaml -- verified: exists with envFrom configMapRef and individual secretKeyRef entries (DATABASE_URL, SECRET_KEY_BASE, KALSHI_*). No Keycloak env vars. Pattern clear for adding KEYCLOAK_CLIENT_SECRET as another secretKeyRef.
    • [x] k8s/base/worker-deployment.yaml -- verified: exists with same env pattern as deployment.yaml. No Keycloak env vars.

    Repo Placement

    OK -- issue filed on ldraney/prediction-assistant. All 7 file targets within prediction-assistant repo k8s/ directory. Single repo.

    Dependencies

    • Upstream (documented): pal-e-services#173 "tofu apply" (board item 1772, todo column, sprint:12) -- provisions prediction-assistant-secrets with KEYCLOAK_CLIENT_SECRET. Documented in Lineage. RESOLVED from previous review.
    • Downstream (documented): prediction-assistant#93 "Verify prediction-assistant.com is live" (board item 1774, todo column, sprint:12). Documented in Lineage.
    • Transitive (implicit, not blocking): pal-e-services#171 "Add Keycloak realm + OIDC client" (board item 1768, validation column, sprint:11) -- realm must exist before env vars are meaningful. Covered transitively through #173.

    Acceptance Criteria

    6 criteria, all verifiable:

    • [x] AC1: "Tailscale Ingress resource created and assigned hostname" -- verifiable via kustomize build output
    • [x] AC2: "HTTPS redirect applied" -- annotated with flexibility (funnel config OR middleware). Verifiable by checking resource/annotation presence.
    • [x] AC3: "Keycloak env vars wired: ConfigMap for KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID" -- verifiable by inspecting configmap.yaml
    • [x] AC4: "KEYCLOAK_CLIENT_SECRET wired from prediction-assistant-secrets Secret" -- verifiable by checking deployment manifest for secretKeyRef
    • [x] AC5: "Pods can start with all required env vars present" -- post-deploy verification, reasonable
    • [x] AC6: "kustomize build k8s/overlays/prod/ succeeds" -- verifiable by agent

    Keycloak env vars cross-verified against app code: omniauth.rb, sessions_controller.rb, and keycloak_admin_service.rb all reference the same 4 env vars (KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET). Issue correctly identifies all 4 with appropriate ConfigMap/Secret separation.

    Blast Radius

    • ConfigMap changes via envFrom propagate to both web and worker deployments -- both listed as file targets. Covered.
    • Adding Keycloak vars to base/ affects both prod and dev overlays. Dev overlay could need different Keycloak URL/realm. Minor future concern -- not a blocker for this ticket.
    • No sibling services in this repo.
    • Existing env var pattern (individual secretKeyRef entries) is well-established -- adding KEYCLOAK_CLIENT_SECRET follows the same pattern as DATABASE_URL, SECRET_KEY_BASE, and KALSHI_* keys.

    Decomposition Assessment

    7 file targets in 1 repo, 6 acceptance criteria. AC count is 1 over the >5 threshold, but ACs 5 and 6 are verification steps (not independent work items). Actual work items: create ingress.yaml, add 3 vars to configmap, add secretKeyRef to 2 deployments, update kustomization resources. All follow existing patterns. Estimated agent work: 3-4 minutes. No decomposition needed.

    Recommendation

    No action needed. All previous NEEDS_REFINEMENT findings resolved. Scope is solid, traceability complete (including backing arch notes), file targets verified, dependencies documented, acceptance criteria clear and testable.

  • Verdict: APPROVED

    Re-review of board item #1813. Previous review (review-1813-2026-07-05) found 2 issues: 5 missing bug template sections and missing arch-edge-proxy architecture note. Both are now resolved.

    Template Completeness

    Issue type: Bug. Checked against template-issue-bug.

    • [x] Type -- present ("Bug")
    • [x] Lineage -- present ("Blocks pal-e-platform#516... Standalone discovery during prediction-assistant S11 deploy")
    • [x] Repo -- present ("ldraney/pal-e-platform")
    • [x] What Broke -- present as "Problem" (semantically equivalent, header name differs slightly)
    • [x] Repro Steps -- present (salt test.ping + salt-minion --version commands)
    • [x] Expected Behavior -- present (test.ping returns True, salt-apply works)
    • [x] Environment -- present (Hetzner CPX11, Debian 12, Tailscale 100.72.199.14)
    • [x] Acceptance Criteria -- present (3 criteria)
    • [x] Related -- present (blocks listed)

    Extra sections: Impact (useful), Solution (useful). All 9 required Bug template sections are present.

    Traceability

    • [x] story:platform-setup label -- Platform Setup
    • [x] story note verified -- platform-setup entry exists in project-prediction-assistant user-stories section (Developer role, "CI/CD push-to-deploy in under 10 minutes")
    • [x] arch:edge-proxy label -- edge proxy component
    • [x] arch note verified -- arch-edge-proxy note exists in pal-e-docs (architecture type, active status, pal-e-platform project). Comprehensive content including diagram, components table, provisioning flow, and "Known Gap: Salt-Minion Not Bootstrapped" section
    • [x] Forgejo issue -- ldraney/pal-e-platform#521, closed (status:approved label)

    File Targets

    • [x] terraform/modules/hetzner-edge/cloud-init.yaml -- verified: exists, contains only Tailscale + Caddy bootstrap (no salt-minion), confirms issue claim
    • [x] salt/bootstrap.sh -- verified: exists, is Arch-only (uses paru, hardcodes MINION_ID="archbox"), confirms issue claim
    • [x] salt/states/top.sls -- verified: line 23 references 'edge-proxy' with caddy state assignment
    • [x] salt/pillar/top.sls -- verified: line 22 references 'edge-proxy'

    Repo Placement

    OK. Issue filed on ldraney/pal-e-platform, fix targets files in pal-e-platform (cloud-init.yaml and/or bootstrap script). Board item is on board-prediction-assistant as a cross-project dependency -- acceptable since this blocks prediction-assistant deployment.

    Dependencies

    • Blocks: pal-e-platform#516 (Apply Caddy salt state) -- board item #1771, currently in validation column
    • Blocks: prediction-assistant#93 (Verify prediction-assistant.com is live) -- board item #1774, currently in todo column
    • Sprint: 12 (deploy + verify)
    • pal-e-services#173 (tofu apply) in todo is also S12 work, not directly dependent but same sprint

    Forgejo issue is now closed with status:approved, indicating implementation has merged. Board item is in validation column awaiting post-merge verification.

    Acceptance Criteria

    3 ACs, all clear and testable:

    • AC1: Salt-minion installed on edge-proxy via IaC -- verifiable by code review of cloud-init or bootstrap script
    • AC2: salt edge-proxy test.ping returns True -- requires SSH to archbox for verification
    • AC3: make salt-apply succeeds for edge-proxy -- requires SSH to archbox

    AC2 and AC3 are integration-level checks requiring remote execution. An agent can implement the code changes but verification requires SSH access to archbox.

    Blast Radius

    • salt/bootstrap.sh is Arch-only. If a Debian bootstrap path is added, future Debian hosts would benefit. No other Debian minions currently in top.sls.
    • Any future Hetzner VPS would have the same cloud-init gap if not updated.
    • arch-edge-proxy note documents the gap under "Known Gap: Salt-Minion Not Bootstrapped" -- good traceability.

    Decomposition Assessment

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

    Recommendations

    No action needed. Both issues from the previous review have been resolved:

    • All 5 missing Bug template sections (Lineage, Repo, Repro Steps, Expected Behavior, Environment) are now present in the Forgejo issue body.
    • The arch-edge-proxy architecture note now exists with comprehensive documentation of the edge proxy component, including the known salt-minion gap.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    Issue type: Bug. Checked against template-issue-bug.

    • [x] Type -- present ("Bug")
    • [ ] Lineage -- MISSING
    • [ ] Repo -- MISSING
    • [x] What Broke -- present as "Problem" (semantically equivalent, header name differs)
    • [ ] Repro Steps -- MISSING
    • [ ] Expected Behavior -- MISSING
    • [ ] Environment -- MISSING
    • [x] Acceptance Criteria -- present (3 criteria)
    • [x] Related -- present (blocks listed)

    Extra sections in issue not in template: Impact (useful), Solution (useful). 5 of 9 required Bug template sections are missing or misnamed.

    Traceability

    • [x] story:platform-setup label -- Platform Setup
    • [x] story note verified -- story-prediction-assistant-platform-setup exists in pal-e-docs (user-story type, active)
    • [x] arch:edge-proxy label -- edge proxy component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-edge-proxy for the Hetzner VPS edge-proxy component
    • [x] Forgejo issue -- ldraney/pal-e-platform#521, open

    File Targets

    • [x] terraform/modules/hetzner-edge/cloud-init.yaml -- verified: exists, contains only Tailscale + Caddy bootstrap (no salt-minion), confirms issue claim
    • [x] salt/bootstrap.sh -- verified: exists, is Arch-only (uses paru, hardcodes MINION_ID="archbox"), confirms issue claim
    • [x] salt/states/top.sls -- verified: references edge-proxy with caddy state assignment
    • [x] salt/pillar/top.sls -- verified: references edge-proxy

    Repo Placement

    OK. Issue filed on ldraney/pal-e-platform, fix targets files in pal-e-platform (cloud-init.yaml and/or bootstrap script). Board item is on board-prediction-assistant as a cross-project dependency -- acceptable since this blocks prediction-assistant deployment.

    Dependencies

    • Blocks: pal-e-platform#516 (Apply Caddy salt state) -- board item #1771, currently in validation column
    • Blocks: prediction-assistant#93 (Verify prediction-assistant.com is live) -- board item #1774, currently in todo column
    • Sprint: 12 (deploy + verify)
    • pal-e-services#173 (tofu apply) in todo is also S12 work, not directly dependent but same sprint

    Note: #1771 (pal-e-platform#516) is in the validation column, suggesting it was merged but validation found the salt-minion prerequisite missing. This is consistent with the bug classification.

    Acceptance Criteria

    3 ACs, all clear and testable:

    • AC1: Salt-minion installed via IaC -- verifiable by code review of cloud-init or bootstrap script
    • AC2: salt edge-proxy test.ping returns True -- requires SSH to archbox for verification
    • AC3: make salt-apply succeeds for edge-proxy -- requires SSH to archbox; note make salt-apply currently runs salt-call state.apply (local), not salt 'edge-proxy' state.apply (remote). May need a targeted salt command instead.

    AC2 and AC3 are integration-level checks requiring remote execution. An agent can implement the code changes but may not be able to verify these without SSH access to archbox.

    Blast Radius

    • salt/bootstrap.sh is Arch-only. If a Debian bootstrap path is added, future Debian hosts would benefit. No other Debian minions currently in top.sls.
    • lucass-macbook-air-1 is also in top.sls with mac-agent state -- different OS, separate concern.
    • Any future Hetzner VPS would have the same cloud-init gap if not updated.

    Decomposition Assessment

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

    Recommendations

    • [BODY] Add missing Bug template sections or restructure to match template-issue-bug: add Lineage (e.g., "Standalone -- discovered during S12 deploy validation"), Repo (ldraney/pal-e-platform), Repro Steps (e.g., "1. SSH to archbox, 2. Run salt edge-proxy test.ping, 3. Observe: minion not found"), Expected Behavior ("salt-minion running on edge-proxy, test.ping returns True"), Environment (Hetzner Debian 12, salt master on archbox at 100.110.151.59)
    • [SCOPE] Create architecture note arch-edge-proxy for the Hetzner VPS edge-proxy component. Multiple board items reference arch:edge-proxy (items #1692, #1726, #1771, #1813) but no backing arch note exists.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [ ] Lineage — MISSING
    • [x] Repo — prediction-assistant
    • [x] User Story — present
    • [x] Context — present
    • [x] File Targets — 7 targets (3 new, 4 existing)
    • [x] Feature Flag — None (acceptable for infra work)
    • [ ] Acceptance Criteria — MISSING (only Test Expectations present)
    • [x] Test Expectations — present (3 items)
    • [x] Constraints — present
    • [x] Checklist — present
    • [x] Related — present (Blocks #93, Sprint 12)

    Traceability

    • [x] story:platform-setup label — Platform Setup
    • [x] story note verified — found in project-prediction-assistant user-stories section
    • [x] arch:k8s-deploy label — k8s deployment component
    • [ ] arch note MISSING — [SCOPE] Create architecture note arch-k8s-deploy for component k8s-deploy
    • [x] arch:keycloak label — Keycloak auth component
    • [ ] arch note MISSING — [SCOPE] Create architecture note arch-keycloak for component keycloak
    • [x] Forgejo issue — ldraney/prediction-assistant#105, open

    File Targets

    • [x] k8s/overlays/prod/ingress.yaml (new) — verified: does not exist, correctly marked as new
    • [x] k8s/overlays/prod/cluster-issuer.yaml (new) — verified: does not exist, correctly marked as new
    • [ ] k8s/overlays/prod/redirect-middleware.yaml (new) — ISSUE: resource type unclear. "Middleware" is a Traefik CRD concept. Issue does not specify which ingress controller is in use or what k8s resource kind this file should contain
    • [x] k8s/overlays/prod/kustomization.yaml — verified: exists, currently has image pin, patches, and labels. New resources must be added to the resources list
    • [x] k8s/base/configmap.yaml — verified: exists with RAILS_ENV, RAILS_LOG_TO_STDOUT, RAILS_SERVE_STATIC_FILES, RAILS_MAX_THREADS, PORT. No Keycloak vars present (confirms the gap)
    • [x] k8s/base/deployment.yaml — verified: exists with envFrom configMapRef and secret env vars (DATABASE_URL, SECRET_KEY_BASE, KALSHI_*). No Keycloak env vars present
    • [x] k8s/base/worker-deployment.yaml — verified: exists with same env pattern as deployment.yaml. No Keycloak env vars present

    Repo Placement

    OK — issue filed on ldraney/prediction-assistant. All 7 file targets are within the prediction-assistant repo k8s/ directory.

    Dependencies

    • Documented: Blocks #93 "Verify prediction-assistant.com is live" (todo column, sprint:12)
    • Undocumented: Depends on pal-e-services#173 "tofu apply — provision prediction-assistant infra" (board item 1772, todo column, sprint:12). The Keycloak env vars reference secrets (KEYCLOAK_CLIENT_SECRET at minimum) that must be provisioned by Terraform before pods can start with correct auth. This dependency is not documented in the issue.
    • Related: pal-e-services#171 "Add Keycloak realm + OIDC client" (validation column, sprint:11) — the Keycloak realm and OIDC client must exist before these env vars are meaningful.

    Acceptance Criteria

    No ### Acceptance Criteria section exists. The Test Expectations section provides partial coverage but is insufficient:

    • "kustomize build k8s/overlays/prod/ succeeds" — verifiable by agent
    • "Ingress routes prediction-assistant.com to service" — only verifiable post-deploy, not by agent
    • "Keycloak env vars present in pod specs" — verifiable via kustomize build output, but does not specify WHICH vars

    Missing criteria:

    • Which specific Keycloak env vars must appear: KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET, KEYCLOAK_URL, KEYCLOAK_REALM (per config/initializers/omniauth.rb)
    • KEYCLOAK_CLIENT_SECRET must come from a Secret (not ConfigMap) — security constraint not stated
    • TLS certificate provisioning verified (ClusterIssuer references valid issuer)
    • HTTP-to-HTTPS redirect behavior

    Blast Radius

    • ConfigMap changes via envFrom propagate to both web and worker deployments — both are listed as file targets, so this is covered
    • Adding Keycloak env vars to base/ affects both prod and dev overlays. If dev should use different Keycloak settings (different realm URL, different client), the issue should specify overlay-level overrides
    • No sibling services in this repo to check for similar patterns

    Decomposition Assessment

    7 file targets in 1 repo, 3 test expectations, estimated agent work ~3-5 minutes. No decomposition needed.

    Recommendation

    • [BODY] Add ### Acceptance Criteria section with verifiable conditions: "kustomize build output contains KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET, KEYCLOAK_URL, KEYCLOAK_REALM in pod specs", "KEYCLOAK_CLIENT_SECRET sourced from Secret not ConfigMap", "Ingress host is prediction-assistant.com", "TLS secretName references cert-manager Certificate"
    • [BODY] Add ### Lineage section (e.g., "Standalone — discovered during deployment audit")
    • [BODY] Specify which Keycloak env vars (KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET, KEYCLOAK_URL, KEYCLOAK_REALM per config/initializers/omniauth.rb) and whether each goes in ConfigMap or Secret (KEYCLOAK_CLIENT_SECRET MUST be a Secret)
    • [BODY] Clarify what k8s resource type redirect-middleware.yaml represents (Traefik Middleware CRD? Ingress annotation? Something else?) and which ingress controller is in use
    • [BODY] Document dependency on tofu apply (pal-e-services#173) for k8s secrets provisioning
    • [SCOPE] Create architecture note arch-k8s-deploy for component k8s-deploy
    • [SCOPE] Create architecture note arch-keycloak for component keycloak
  • Verdict: APPROVED

    Re-review of board item #1775. Previous review (review-1775-2026-07-05) found one [SCOPE] issue: arch-ios note was scoped only to landscaping-assistant. That has been resolved.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, modeled after landscaping-assistant-ios
    • [x] Repo -- ldraney/prediction-assistant-ios
    • [x] User Story -- present
    • [x] Context -- present
    • [x] File Targets -- 15 files to create, 1 exclusion
    • [x] Feature Flag -- "none" (correct for iOS scaffold)
    • [x] Acceptance Criteria -- 5 criteria
    • [x] Test Expectations -- present with run command
    • [x] Constraints -- present
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:app-experience label -- App Experience
    • [x] story note verified -- found in project-prediction-assistant user-stories section
    • [x] arch:ios label -- iOS / Turbo Native
    • [x] arch note verified -- arch-ios note exists in pal-e-docs, now cross-project with "Projects Using This Pattern" section covering both landscaping-assistant-ios and prediction-assistant-ios. Has prediction-assistant tag.
    • [x] Forgejo issue -- ldraney/prediction-assistant-ios#2, open

    File Targets

    • [x] project.yml -- verified: matches landscaping-assistant-ios
    • [x] PredictionAssistant/AppDelegate.swift -- verified: matches LandscapingAssistant/AppDelegate.swift
    • [x] PredictionAssistant/SceneDelegate.swift -- verified: matches LandscapingAssistant/SceneDelegate.swift
    • [x] PredictionAssistant/Info.plist -- verified: matches LandscapingAssistant/Info.plist
    • [x] PredictionAssistant/Assets.xcassets/ -- verified: matches LandscapingAssistant/Assets.xcassets/
    • [x] fastlane/Appfile -- verified: exists in reference repo
    • [x] fastlane/Fastfile -- verified: exists in reference repo
    • [x] fastlane/Deliverfile -- verified: exists in reference repo
    • [x] fastlane/metadata/ -- verified: exists in reference repo with review_information/, en-US/, copyright.txt
    • [x] fastlane/rating_config.json -- verified: exists in reference repo
    • [x] build-testflight.sh -- verified: exists in reference repo
    • [x] scripts/export-certs.sh -- verified: exists in reference repo
    • [x] scripts/fix-keychain.sh -- verified: exists in reference repo
    • [x] scripts/import-ci-keychain.sh -- verified: exists in reference repo
    • [x] .gitignore -- verified: exists in reference repo

    Target repo (prediction-assistant-ios) confirmed to contain only README.md currently. All 15 file targets verified against the landscaping-assistant-ios reference repo tree.

    Repo Placement

    OK. Forgejo issue filed on ldraney/prediction-assistant-ios. Board item links to the same. All work is in one repo. No cross-repo concerns.

    Dependencies

    • #1774 "Verify prediction-assistant.com is live" (sprint:12, backlog) -- direct prerequisite. Issue Lineage states "prediction-assistant.com must be live first." Sprint ordering is correct (S12 before S13).
    • #1772 "tofu apply -- provision prediction-assistant infra" (sprint:12, backlog) -- upstream of #1774.
    • #1776 "TestFlight beta build on MacBook" (sprint:13, backlog) -- downstream, depends on this ticket completing first.

    Sprint ordering (S12 infra, then S13 iOS) is correct. Dependencies are implied by sprint labels and the Lineage section but not explicitly listed in the issue body. Acceptable given the Lineage statement.

    Acceptance Criteria

    5 criteria, all testable:

    • AC 1-2: xcodegen + Xcode build -- verifiable locally with the provided run command
    • AC 3: App loads prediction-assistant.com -- requires live site (S12 prerequisite satisfied by sprint ordering)
    • AC 4: Fastlane config -- verifiable by inspecting Appfile contents
    • AC 5: Keycloak login in web view -- requires live site + Keycloak realm (S12 prerequisite)

    Test command (xcodegen generate && xcodebuild -scheme PredictionAssistant -sdk iphonesimulator build) is real and executable on macOS. Note: AC 3 and 5 can only be fully validated after S12 deployment completes.

    Blast Radius

    Low. Isolated iOS scaffold repo. No downstream consumers beyond the TestFlight build ticket (#1776). Pattern already proven with landscaping-assistant-ios. Rails-side Turbo Native support already in place per docs/platform/turbo-native-pipeline.md.

    Decomposition Assessment

    15 file targets in 1 repo. 5 acceptance criteria (at limit, not over). Estimated agent work: copy-and-adapt from landscaping-assistant-ios reference repo. This is scaffolding work -- no complex logic, no multi-system coordination. Under 5 minutes. No decomposition needed.

    Previous Review Findings -- Resolution

    • [SCOPE] arch-ios note scoped only to landscaping-assistant -- RESOLVED. The arch-ios note now has a "Projects Using This Pattern" section listing both landscaping-assistant-ios and prediction-assistant-ios, and carries the prediction-assistant tag.

    Recommendation

    No action needed.

  • Verdict: READY

    Re-review after refinements applied. All issues from review-1774-2026-07-05 have been resolved: type corrected to Task, File Targets replaced with Scope section, arch-k8s-deploy note created.

    Template Completeness

    • [x] Type — Task
    • [x] Lineage — dependencies listed
    • [x] Repo — ldraney/prediction-assistant
    • [x] User Story — present
    • [x] Context — clear motivation (end-to-end deploy verification)
    • [x] Scope — verification-only, no code changes
    • [x] Feature Flag — none (appropriate)
    • [x] Acceptance Criteria — 5 criteria
    • [x] Test Expectations — manual browser + curl command
    • [x] Constraints — upstream infra dependency noted
    • [x] Checklist — present
    • [x] Related — project reference

    Traceability

    • [x] story:app-experience label — App Experience
    • [x] story note verified — found in project-prediction-assistant user-stories section
    • [x] arch:k8s-deploy label — Kubernetes Deployment
    • [x] arch note verified — arch-k8s-deploy note exists in pal-e-docs (active, with full Mermaid diagram and component table)
    • [x] Forgejo issue — ldraney/prediction-assistant#93, open

    File Targets

    N/A — Task type with no code changes. Scope section correctly describes verification-only work.

    Repo Placement

    OK — Issue filed on ldraney/prediction-assistant, matches Repo field. Verification task touches no code.

    Dependencies

    Lineage section documents: "Depends on: all infra provisioned, CI green, Caddy applied, ArgoCD synced." Board items confirm:

    • #1772 — tofu apply (sprint:12, backlog) — direct prerequisite
    • #1773 — Woodpecker activation (sprint:12, backlog) — direct prerequisite
    • #1771 — Caddy salt state (sprint:11, validation) — direct prerequisite
    • #1767 — services terraform (sprint:11, validation) — upstream
    • #1768 — Keycloak realm (sprint:11, validation) — upstream
    • #1770 — .woodpecker.yaml CI pattern (sprint:11, validation) — upstream

    All upstream work is in validation or backlog. Ticket correctly sits in backlog until prerequisites complete.

    Acceptance Criteria

    5 criteria, all verifiable by an agent:

    • curl returns 200 — direct CLI check
    • Landing page renders — browser or curl verification
    • Login redirects to Keycloak — HTTP redirect inspection
    • Keycloak login succeeds — requires test credentials (documented in Keycloak realm)
    • ArgoCD shows Synced + Healthy — CLI or API check

    Test command provided: curl -sI https://prediction-assistant.com — real and executable.

    Blast Radius

    None. Verification-only task with no code changes. No downstream consumers affected.

    Decomposition Assessment

    No decomposition needed. Task type with 0 file targets, 5 acceptance criteria (all simple verification steps), estimated agent work under 2 minutes.

    Recommendation

    No action needed.

  • Review: Scaffold Turbo Native iOS app review-1775-2026-07-05

    Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Standalone, modeled after landscaping-assistant-ios
    • [x] Repo — ldraney/prediction-assistant-ios
    • [x] User Story — present
    • [x] Context — present
    • [x] File Targets — 15 files to create, 1 exclusion
    • [x] Feature Flag — "none" (correct for iOS scaffold)
    • [x] Acceptance Criteria — 5 criteria
    • [x] Test Expectations — present with run command
    • [x] Constraints — present
    • [x] Checklist — present
    • [x] Related — present

    Traceability

    • [x] story:app-experience label — App Experience
    • [x] story note verified — found in project-prediction-assistant user-stories section
    • [x] arch:ios label — iOS / Turbo Native
    • [ ] arch note PARTIAL — arch-ios note exists in pal-e-docs but is scoped to landscaping-assistant project, not prediction-assistant. [SCOPE] Create or update arch-ios note for prediction-assistant iOS component
    • [x] Forgejo issue — ldraney/prediction-assistant-ios#2, open

    File Targets

    • [x] project.yml — verified: matches landscaping-assistant-ios
    • [x] PredictionAssistant/AppDelegate.swift — verified: matches LandscapingAssistant/AppDelegate.swift
    • [x] PredictionAssistant/SceneDelegate.swift — verified: matches LandscapingAssistant/SceneDelegate.swift
    • [x] PredictionAssistant/Info.plist — verified: matches LandscapingAssistant/Info.plist
    • [x] PredictionAssistant/Assets.xcassets/ — verified: matches LandscapingAssistant/Assets.xcassets/
    • [x] fastlane/Appfile — verified: exists in reference repo
    • [x] fastlane/Fastfile — verified: exists in reference repo
    • [x] fastlane/Deliverfile — verified: exists in reference repo
    • [x] fastlane/metadata/ — verified: exists in reference repo with review_information/, en-US/, copyright.txt
    • [x] fastlane/rating_config.json — verified: exists in reference repo
    • [x] build-testflight.sh — verified: exists in reference repo
    • [x] scripts/export-certs.sh — verified: exists in reference repo
    • [x] scripts/fix-keychain.sh — verified: exists in reference repo
    • [x] scripts/import-ci-keychain.sh — verified: exists in reference repo
    • [x] .gitignore — verified: exists in reference repo

    Target repo (prediction-assistant-ios) confirmed to contain only README.md currently. All 15 file targets verified against the landscaping-assistant-ios reference repo tree.

    Repo Placement

    OK. Forgejo issue filed on ldraney/prediction-assistant-ios. Board item links to the same. All work is in one repo. No cross-repo concerns.

    Dependencies

    • #1774 "Verify prediction-assistant.com is live" (sprint:12, backlog) — direct prerequisite. Issue Lineage states "prediction-assistant.com must be live first." Sprint ordering is correct (S12 before S13).
    • #1772 "tofu apply — provision prediction-assistant infra" (sprint:12, backlog) — upstream of #1774.
    • #1776 "TestFlight beta build on MacBook" (sprint:13, backlog) — downstream, depends on this ticket completing first.

    Sprint ordering (S12 infra, then S13 iOS) is correct. Dependencies are implied by sprint labels and the Lineage section but not explicitly listed in the issue body. Acceptable given the Lineage statement.

    Acceptance Criteria

    5 criteria, all testable:

    • AC 1-2: xcodegen + Xcode build — verifiable locally with the provided run command
    • AC 3: App loads prediction-assistant.com — requires live site (S12 prerequisite satisfied by sprint ordering)
    • AC 4: Fastlane config — verifiable by inspecting Appfile contents
    • AC 5: Keycloak login in web view — requires live site + Keycloak realm (S12 prerequisite)

    Test command (xcodegen generate && xcodebuild -scheme PredictionAssistant -sdk iphonesimulator build) is real and executable on macOS. Note: AC 3 and 5 can only be fully validated after S12 deployment completes.

    Blast Radius

    Low. Isolated iOS scaffold repo. No downstream consumers beyond the TestFlight build ticket (#1776). Pattern already proven with landscaping-assistant-ios. Rails-side Turbo Native support already in place per docs/platform/turbo-native-pipeline.md.

    Decomposition Assessment

    15 file targets in 1 repo. 5 acceptance criteria (at limit, not over). Estimated agent work: copy-and-adapt from landscaping-assistant-ios reference repo. This is scaffolding work — no complex logic, no multi-system coordination. Under 5 minutes. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note for prediction-assistant iOS component. The existing arch-ios note is scoped to landscaping-assistant. Either create a prediction-assistant-specific arch note or update arch-ios to be cross-project with project-specific sections.
  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- "Depends on: iOS scaffold complete, prediction-assistant.com live."
    • [x] Repo -- ldraney/prediction-assistant-ios
    • [x] User Story -- present
    • [x] Context -- present
    • [x] File Targets -- "none -- execution task using existing Fastlane config"
    • [x] Feature Flag -- "none"
    • [x] Acceptance Criteria -- 4 items
    • [x] Test Expectations -- present
    • [x] Constraints -- present
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:app-experience label -- App Experience (Consumer)
    • [x] story note verified -- found in project-prediction-assistant user-stories section
    • [x] arch:ios label -- iOS (turbo-ios) component
    • [x] arch note verified -- arch-ios note exists in pal-e-docs (under landscaping-assistant project, same Turbo Native stack). Prediction-assistant also has comprehensive docs/platform/turbo-native-pipeline.md covering iOS architecture.
    • [x] Forgejo issue -- ldraney/prediction-assistant-ios#3, open

    File Targets

    • [x] No file modifications -- this is an execution task (run fastlane beta on MacBook)
    • [x] build-testflight.sh -- referenced in Test Expectations; confirmed this file is listed in scaffold ticket (issue #2) File Targets
    • [x] fastlane/ -- correctly marked as "do not touch" (created by scaffold ticket)

    Repo Placement

    OK. Forgejo issue filed on ldraney/prediction-assistant-ios, matching the Repo section. Work is an execution task on the iOS repo. No other repos affected.

    Dependencies

    • Board item #1775: "Scaffold Turbo Native iOS app" (backlog, sprint:13) -- must complete first. Creates fastlane config, build-testflight.sh, and XcodeGen project.
    • Board item #1774: "Verify prediction-assistant.com is live" (backlog, sprint:12) -- must complete first. App loads prediction-assistant.com in the native web view.
    • Board item #1772: "tofu apply -- provision prediction-assistant infra" (backlog, sprint:12) -- transitive dependency via #1774.
    • All dependencies documented in Lineage section.

    Acceptance Criteria

    4 criteria, all reasonable for an iOS deployment ticket. Criterion 1 (fastlane beta completes) is agent-verifiable via exit code. Criteria 2-4 (App Store Connect, TestFlight install, login works) require manual verification, which is appropriate for a physical-device deployment task. Test command references SSH to MacBook which matches Constraints section.

    Blast Radius

    Low. This is a build-and-upload execution task with no code changes. No downstream code consumers affected. The only external system touched is App Store Connect / TestFlight.

    Decomposition Assessment

    No decomposition needed. Zero file targets (execution task), 4 acceptance criteria, 1 repo, estimated agent work well under 5 minutes for the build trigger. Manual verification steps are inherent to iOS deployment and cannot be decomposed further.

    Recommendation

    No action needed.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type
    • [x] Lineage
    • [x] Repo
    • [x] User Story
    • [x] Context
    • [x] File Targets (states "none — verification only")
    • [x] Feature Flag
    • [x] Acceptance Criteria
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    All sections present. However, see [BODY] recommendation below regarding Type classification.

    Traceability

    • [x] story:app-experience label — App Experience
    • [x] story note verified — story-prediction-assistant-app-experience exists (active) and is listed in project-prediction-assistant user-stories section
    • [x] arch:k8s-deploy label — k8s deployment component
    • [ ] arch note MISSING — [SCOPE] Create architecture note arch-k8s-deploy for the Kubernetes deployment component
    • [x] Forgejo issue — ldraney/prediction-assistant#93, open

    File Targets

    • [x] No file modifications — verification-only ticket. k8s manifests exist at k8s/base/deployment.yaml, k8s/overlays/prod/ingress.yaml, and config/environments/production.rb references prediction-assistant.com. No changes needed.

    Repo Placement

    Filed on ldraney/prediction-assistant, matches Repo field. Verification touches multiple systems (DNS/Terraform in pal-e-services, Caddy in pal-e-platform, k8s in prediction-assistant) but since no code changes are made, single-repo placement is appropriate.

    Dependencies

    Lineage states "Depends on: all infra provisioned, CI green, Caddy applied, ArgoCD synced." Specific board dependencies:

    • S12 peers (backlog): #1772 tofu apply (pal-e-services#173), #1773 Woodpecker activation (prediction-assistant#92) — both still in backlog, must complete first
    • S11 upstream (validation): #1767 services terraform, #1768 Keycloak realm, #1769 CNPG database, #1770 Woodpecker YAML, #1771 Caddy salt state — all in validation column

    Dependencies are captured at intent level in Lineage but not as specific board item references.

    Acceptance Criteria

    5 criteria, all verifiable by an agent:

    • curl -sI https://prediction-assistant.com — real command, verifies HTTP 200
    • Landing page render — verifiable via browser automation / screenshot
    • Keycloak login redirect — verifiable via browser automation
    • Keycloak login success + redirect back — verifiable (requires test credentials)
    • ArgoCD Synced + Healthy — verifiable via ArgoCD CLI or UI

    Criteria are clear and testable. No missing criteria identified.

    Blast Radius

    Minimal. Verification-only ticket with no code changes. Validates the full deploy pipeline end-to-end (DNS → Caddy → Tailscale → k8s → Rails). No downstream consumers affected.

    Decomposition Assessment

    0 file targets, 5 acceptance criteria, single repo, estimated agent work under 5 minutes. No decomposition needed.

    Recommendation

    • [BODY] Change ### Type from "Feature" to "Task" — this is purely verification with no code changes. Per template-issue convention, Task type uses ### Scope instead of ### File Targets. Replace the File Targets section with a Scope section describing the verification steps.
    • [SCOPE] Create architecture note arch-k8s-deploy for the Kubernetes deployment component in pal-e-docs. This note should cover the k8s manifest structure, overlay strategy, ArgoCD sync, and ingress configuration.
  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- documents dependency on .woodpecker.yaml + tofu apply
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- present
    • [x] Context -- present, sufficient background
    • [x] File Targets -- "none" (correctly identified as Woodpecker UI/API task)
    • [x] Feature Flag -- "none" (appropriate for infra task)
    • [x] Acceptance Criteria -- 4 criteria, all verifiable
    • [x] Test Expectations -- 2 manual verifications
    • [x] Constraints -- 2 constraints documented
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:platform-setup label -- Platform Setup
    • [x] story note verified -- found in project-prediction-assistant user-stories section
    • [x] arch:ci-pipeline label -- CI Pipeline
    • [x] arch note verified -- arch-ci-pipeline note exists in pal-e-docs (project: pal-e-platform)
    • [x] Forgejo issue -- ldraney/prediction-assistant#92, state: open

    File Targets

    • [x] No file targets claimed -- correct, this is a Woodpecker API/UI activation task
    • [x] .woodpecker.yaml verified to exist -- references harbor_username/harbor_password from_secret, consistent with AC
    • [x] Dockerfile verified to exist -- referenced by .woodpecker.yaml build-and-push step

    Repo Placement

    OK. Issue filed on ldraney/prediction-assistant, which is the repo to be activated in Woodpecker. The work itself is Woodpecker API/UI operations, not code changes to the repo. Single repo, no cross-repo concerns.

    Dependencies

    • #1770 "Update .woodpecker.yaml to match platform CI pattern" -- validation column (prerequisite complete)
    • #1772 "tofu apply -- provision prediction-assistant infra" -- backlog, sprint:12 (sequentially blocks this ticket; Harbor project + CI robot credentials come from tofu output)
    • #1767 "Add prediction-assistant to services terraform" -- validation (prerequisite for #1772, complete)

    Dependency chain is correctly ordered: #1767 (validation) -> #1772 (backlog) -> #1773 (backlog). Lineage section documents the dependency accurately.

    Acceptance Criteria

    4 criteria, all agent-verifiable:

    • "Repo appears in Woodpecker active repos list" -- verifiable via Woodpecker API (list_repos)
    • "harbor_username and harbor_password secrets configured" -- verifiable via Woodpecker API (list_repo_secrets)
    • "First pipeline on main completes green" -- verifiable via Woodpecker API (list_pipelines + get_pipeline_status)
    • "Image prediction-assistant/app:<sha> exists in Harbor" -- verifiable via Harbor API

    Note: the pipeline also uses forgejo_user/forgejo_password global secrets for clone. These are global Woodpecker secrets shared across all repos and should already exist. Not a gap in scope.

    Blast Radius

    Minimal. This is a Woodpecker activation + secret creation task. No code changes to the repo. Only affects prediction-assistant CI pipeline. No downstream consumers affected.

    Decomposition Assessment

    0 file targets, 4 acceptance criteria, estimated < 5 minutes of agent work. No decomposition needed.

    Recommendation

    No action needed.

  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- dependencies on services terraform, Keycloak, CNPG database
    • [x] Repo -- ldraney/pal-e-services
    • [x] User Story -- present
    • [x] Context -- present, explains post-merge apply workflow
    • [x] File Targets -- present (correctly states "none" for execution task)
    • [x] Feature Flag -- none (correct for infra work)
    • [x] Acceptance Criteria -- 5 items, all verifiable
    • [x] Test Expectations -- present with run command
    • [x] Constraints -- present (merge order, kubeconfig access)
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:platform-setup label -- Platform Setup
    • [x] story note verified -- story-prediction-assistant-platform-setup exists in pal-e-docs (user-story, active, project: prediction-assistant). Listed in project-prediction-assistant user-stories section.
    • [x] arch:iac label -- Infrastructure as Code
    • [x] arch note verified -- arch-iac note exists (type: architecture, status: active, project: prediction-assistant). Contains Mermaid diagram, components table, and key decisions.
    • [x] Forgejo issue -- ldraney/pal-e-services#173, open

    File Targets

    Issue correctly states "none" -- this is a tofu apply execution task with no file modifications. All terraform files are already merged via upstream dependencies.

    Repo Placement

    OK. Issue filed on ldraney/pal-e-services, which is where tofu apply runs. The terraform directory and tfvars live in pal-e-services. Correct placement.

    Dependencies

    • Upstream (documented in Lineage):
      • Board #1767: "Add prediction-assistant to services terraform" (pal-e-services#170) -- validation column (merged)
      • Board #1768: "Add Keycloak realm + OIDC client" (pal-e-services#171) -- validation column (merged)
      • Board #1769: "Add prediction-assistant database to CNPG" (pal-e-services#172) -- validation column (merged)
    • Downstream (not documented but inferred):
      • Board #1774: "Verify prediction-assistant.com is live" (sprint:12) -- depends on infra being provisioned
      • Board #1773: "Activate repo in Woodpecker + first green build" (sprint:12) -- may depend on namespace/Harbor existing

    All upstream dependencies are in validation column (merged, awaiting validation). Lineage section correctly identifies them.

    Acceptance Criteria

    5 criteria, all machine-verifiable:

    • [x] tofu apply exit code -- directly testable
    • [x] kubectl get ns prediction-assistant -- CLI command, verifiable
    • [x] ArgoCD application check -- verifiable via argocd app get or kubectl
    • [x] Harbor project + robot accounts -- verifiable via Harbor API
    • [x] Keycloak realm and client -- verifiable via Keycloak admin API

    All criteria are testable by an agent with cluster access.

    Blast Radius

    Low. This provisions NEW resources (namespace, Harbor project, ArgoCD app, Keycloak realm, CNPG database) for the prediction-assistant service. No modifications to existing services. Tofu modules are idempotent. Other services in the same cluster are unaffected.

    Decomposition Assessment

    No decomposition needed. Zero file targets, 5 acceptance criteria (at threshold), single command execution (tofu apply) plus verification. Well under 5-minute rule.

    Recommendation

    No action needed. Scope is solid, traceability complete, dependencies documented and merged.

    Minor observations (non-blocking):

    • The arch-iac note heading still references "kalshi-assistant" (old repo name) rather than "prediction-assistant". Not blocking for this ticket but worth a doc cleanup pass.
    • Issue type is "Feature" but the work is an execution task (no code changes). Functionally correct -- the Checklist item "PR opened" does not apply since there is no PR for a tofu apply. Non-blocking.
  • Verdict: APPROVED

    Re-review of board item #1779. Previous review (review-1779-2026-07-04) flagged 4 issues: wrong story label, missing arch note, vague file targets, repo placement mismatch. All 4 have been resolved.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- present, references S7 QA of PR #96 (issue #88)
    • [x] Repo -- present, with explicit cross-repo note
    • [x] User Story -- present
    • [x] Context -- present, clear explanation of schema.rb drift problem
    • [x] File Targets -- present, now specific (see below)
    • [x] Feature Flag -- None (correct, documentation-only change)
    • [x] Acceptance Criteria -- present, 3 items
    • [x] Test Expectations -- present (no code tests needed)
    • [x] Constraints -- present, sensible guardrails
    • [x] Checklist -- present
    • [x] Related -- present, references PR #96 and PR #75
    • [x] Points -- 1

    Traceability

    • [x] story:platform-setup label -- "Platform Setup" (Developer, CI/CD push-to-deploy in under 10 minutes). Previously was story:bot-marketplace (FIXED).
    • [x] story note verified -- found in project-prediction-assistant user-stories section
    • [x] arch:rails label -- Rails architecture component
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (active, architecture type, project prediction-assistant). Previously missing (FIXED).
    • [x] Forgejo issue -- ldraney/prediction-assistant#99, open

    File Targets

    • [x] ~/claude-custom/agents/dev.md -- verified: file exists (4.3k), already has "Infrastructure Enforcement" and "Python Enforcement" sections where a "Rails Enforcement" section fits naturally. Previously vague ("Dev agent type definition (if applicable)") -- now specific (FIXED).
    • [x] pal-e-docs agent-workflow note -- secondary deliverable, note to be created. Acceptable for a 1-point documentation task.

    Repo Placement

    Issue filed on ldraney/prediction-assistant, primary deliverable in ldraney/claude-custom (agents/dev.md). Cross-repo note in issue body explicitly documents this: "This issue is filed on prediction-assistant because the drift was discovered here (PR #96), but the primary deliverable lives in claude-custom." Previously undocumented (FIXED).

    Dependencies

    No blockers found. No items in in_progress column. This is a standalone process improvement with no upstream or downstream dependencies.

    Acceptance Criteria

    3 acceptance criteria, all verifiable by an agent post-implementation:

    • AC1: "Dev agents diff schema.rb against base branch before committing" -- verifiable by reading dev.md for the new section
    • AC2: "If unrelated changes detected, regenerate from clean db:schema:load && db:migrate cycle" -- clear recovery procedure documented in the section
    • AC3: "Document the check in dev agent workflow" -- verifiable by reading dev.md and optionally the pal-e-docs note

    Blast Radius

    Global fix in dev.md benefits all 7 Rails repos (prediction-assistant, westside-basketball, flightscanner, pal-enterprises, palinks, landscaping-assistant, believers-elite). No downstream consumers affected -- documentation only.

    Decomposition Assessment

    1 point, 2 file targets (documentation only), 3 acceptance criteria. Estimated agent work well under 5 minutes. No decomposition needed.

    Recommendation

    No action needed. All previously flagged issues have been resolved.

    Previous Review Issues -- Resolution Status

    • [x] [LABEL] story:bot-marketplace changed to story:platform-setup -- RESOLVED
    • [x] [SCOPE] arch-rails note created in pal-e-docs -- RESOLVED
    • [x] [BODY] File targets now specify exact path ~/claude-custom/agents/dev.md -- RESOLVED
    • [x] [BODY] Cross-repo note added explaining repo placement -- RESOLVED
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — present, references S7 QA of PR #96
    • [x] Repo — ldraney/prediction-assistant
    • [x] User Story — present
    • [x] Context — present, clear explanation of the drift problem
    • [x] File Targets — present but vague (see below)
    • [x] Feature Flag — None (correct, documentation-only change)
    • [x] Acceptance Criteria — present, 3 items
    • [x] Test Expectations — present (no code tests needed)
    • [x] Constraints — present, sensible guardrails
    • [x] Checklist — present
    • [x] Related — present, references PR #96 and PR #75

    Traceability

    • [ ] story:bot-marketplace label — WRONG STORY. This is a dev agent process improvement discovered during QA. It has no relationship to the bot-marketplace user story ("User activates first bot within 2 minutes of login"). The discovery context (issue #88) was story:portfolio-builder work, and the fix is foundational process improvement. [LABEL] Change to story:platform-setup or remove (foundational work, acceptable without story).
    • [ ] story note verification — bot-marketplace exists on project-prediction-assistant user-stories section, but the label itself is wrong per above.
    • [ ] arch:rails label — present, but no backing architecture note found. search_notes("arch-rails") returned empty. [SCOPE] Create architecture note arch-rails for the Rails component.
    • [x] Forgejo issue — ldraney/prediction-assistant#99, open

    File Targets

    • [ ] "CLAUDE.md or agent-workflow pal-e-docs note" — ISSUE: Vague. CLAUDE.md in prediction-assistant is a symlink to README.md (a docs index, not an instructions file). There is no project-level .claude/CLAUDE.md. No agent-workflow note exists in pal-e-docs (search returned empty). The ticket must specify which file and where. [BODY] Specify exact target: either create a .claude/CLAUDE.md in prediction-assistant with the schema check, or add a Rails enforcement section to ~/.claude/agents/dev.md (lives in claude-custom repo).
    • [ ] "Dev agent type definition (if applicable)" — ISSUE: Vague. The dev agent definition exists at ~/.claude/agents/dev.md in the claude-custom repo (not prediction-assistant). It already has domain-specific enforcement sections (Infrastructure, Python) where a "Rails Enforcement" section with the schema.rb check would fit naturally. [BODY] Clarify: ~/.claude/agents/dev.md in ldraney/claude-custom repo.

    Repo Placement

    Issue is filed on ldraney/prediction-assistant, but the most impactful fix location is ~/.claude/agents/dev.md which lives in the ldraney/claude-custom repo. If the fix only goes in prediction-assistant's CLAUDE.md, it is too narrow — there are 7 Rails projects with schema.rb files (prediction-assistant, westside-basketball, flightscanner, pal-enterprises, palinks, landscaping-assistant, believers-elite) that would all benefit from the check. Adding a "Rails Enforcement" section to dev.md (global) would cover all repos. [BODY] Clarify repo placement: if dev.md in claude-custom is the target, file the issue there or document that the fix is cross-repo.

    Dependencies

    No blockers found. This is a standalone process improvement. No board items are blocking or blocked by this ticket.

    Acceptance Criteria

    3 acceptance criteria, all reasonable:

    • AC1: "Dev agents diff schema.rb against the base branch before committing" — testable but lacks specificity on implementation mechanism (guideline in CLAUDE.md? section in dev.md? a hook?). The Constraints section says "guideline, not a blocking hook" which helps.
    • AC2: "If unrelated changes detected, regenerate from clean db:schema:load && db:migrate cycle" — clear recovery procedure, testable.
    • AC3: "Document the check in the dev agent workflow" — testable but vague on location (matches the file targets vagueness).

    Blast Radius

    7 Rails repos with schema.rb: prediction-assistant, westside-basketball, flightscanner, pal-enterprises, palinks, landscaping-assistant, believers-elite. A global fix in dev.md benefits all. A project-scoped fix in prediction-assistant only benefits one repo. No downstream consumers affected — this is documentation only.

    Decomposition Assessment

    1 point, 2-3 file targets (documentation only), 3 acceptance criteria. Estimated agent work well under 5 minutes. No decomposition needed.

    Recommendation

    • [LABEL] Change story:bot-marketplace to story:platform-setup (or remove story label — foundational process work is acceptable without a user story).
    • [SCOPE] Create architecture note arch-rails for the Rails component in pal-e-docs.
    • [BODY] Resolve file target ambiguity: specify whether the fix goes in (a) ~/.claude/agents/dev.md as a "Rails Enforcement" section (global, covers all 7 Rails repos, lives in claude-custom repo), or (b) prediction-assistant/.claude/CLAUDE.md (project-scoped, narrow). Option (a) is recommended given 7 affected repos.
    • [BODY] If option (a), update the Repo field to ldraney/claude-custom or note that the fix is cross-repo. If option (b), create the .claude/CLAUDE.md file path since it does not currently exist.
  • Verdict: READY

    Pass 3 — 2026-07-04. Prior passes had conflicting findings about whether the entry already exists. Verified directly: it does NOT exist in k3s.tfvars. Issue body corrected to say "add new entry".

    Template Completeness

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

    Traceability

    • [x] story:platform-setup label — verified in project-prediction-assistant user-stories table
    • [x] story note verified — "Platform Setup" entry exists on project-prediction-assistant user-stories section (key: platform-setup, role: Developer (Lucas))
    • [x] arch:iac label — present on board item
    • [ ] arch note MISSING — arch-iac note does not exist in pal-e-docs. Known platform-wide documentation gap; not blocking per review policy
    • [x] Forgejo issue — ldraney/pal-e-services#170, open

    File Targets

    • [x] terraform/k3s.tfvars — verified: file exists (symlinked from ~/secrets/pal-e-services/k3s.tfvars). Confirmed prediction-assistant entry does NOT exist (grep returns empty). Services map starts at line 218. Existing pattern clear (landscaping-assistant at line 228 is the closest Rails analog: port 3000, funnel true).
    • [x] terraform/services.tf — verified: for_each loop at line 156. coalesce(each.value.source_repo, each.value.forgejo_repo) at line 181 correctly handles null source_repo by falling back to forgejo_repo. Agent correctly told NOT to touch.
    • [x] terraform/variables.tf — verified: service type definition at lines 234-247. All required fields present (forgejo_repo, image_repo, port, funnel, source_repo optional, source_path optional). Agent correctly told NOT to touch.
    • [x] k8s/overlays/prod/ — verified: directory exists in prediction-assistant repo with kustomization.yaml, ingress.yaml, cluster-issuer.yaml, redirect-middleware.yaml. source_path value "k8s/overlays/prod" is accurate.

    Repo Placement

    Correct. Issue filed on pal-e-services (#170), which is where the services terraform and k3s.tfvars live. Single-repo change — no cross-repo coordination needed.

    Dependencies

    • #1769 "Add prediction-assistant database to CNPG" (backlog, sprint:9) — sibling, not a blocker. Services entry is independent of database provisioning.
    • #1768 "Add Keycloak realm + OIDC client" (backlog, sprint:9) — sibling, not a blocker. Keycloak and services are separate terraform resources.
    • #1772 "tofu apply — provision prediction-assistant infra" (backlog, sprint:10) — downstream dependency. Correctly sequenced one sprint later. Depends on this ticket completing first.
    • #1770 "Update .woodpecker.yaml to match platform CI pattern" (backlog, sprint:9) — sibling, independent.

    No undocumented blockers. Sprint sequencing (sprint:9 before sprint:10) implicitly handles the dependency chain.

    Acceptance Criteria

    All 4 criteria are concrete and agent-verifiable:

    • [x] AC1: "prediction-assistant entry exists in services map without source_repo" — verifiable via grep. Omitting source_repo is a supported pattern: coalesce() falls back to forgejo_repo (line 181 of services.tf). Precedent: platform-validation entry in k3s.tfvars.example also omits source_repo.
    • [x] AC2: "source_path is k8s/overlays/prod" — verifiable via grep. Path confirmed to exist in the prediction-assistant repo with kustomization.yaml.
    • [x] AC3: "tofu plan shows new namespace, Harbor project, robot accounts, ArgoCD application, Tailscale funnel" — verifiable via tofu plan output.
    • [x] AC4: "No errors on plan" — verifiable via exit code.

    Test command cd terraform && tofu plan is real and appropriate.

    Blast Radius

    Low. Adding a new entry to the services map is additive — it cannot break existing services. The for_each loop processes each service independently. The image-updater annotation conditional (lines 171-173 of services.tf) correctly omits the kustomization write-back-target when source_repo is null, which is appropriate for a service whose k8s manifests live in the app repo. No similar bug pattern to propagate — this is a new entry, not a fix.

    Decomposition Assessment

    • 1 file target across 1 repo — below threshold
    • 4 acceptance criteria — below threshold
    • Estimated agent work: under 2 minutes (add ~8 lines to a tfvars map, run tofu plan)

    No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-iac for the IaC component — platform-wide gap, not blocking this ticket. Track as a separate documentation item.

    No other action needed. Scope is solid.

  • Verdict: READY

    Template Completeness

    Checked against template-issue-feature (Type: Feature).

    • [x] Type
    • [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

    Traceability

    • [x] story:platform-setup label -- "Platform Setup"
    • [x] story note verified -- story-prediction-assistant-platform-setup exists in project-prediction-assistant user-stories section
    • [x] arch:ci-pipeline label -- CI pipeline component
    • [ ] arch note MISSING -- no arch-ci-pipeline note found in pal-e-docs. Non-blocking: platform-wide documentation gap, not specific to this ticket. [SCOPE] Create architecture note arch-ci-pipeline for component ci-pipeline.
    • [x] Forgejo issue -- #91, open

    File Targets

    • [x] .woodpecker.yaml -- verified: exists at repo root, contains bare-bones config (ruby:3.4, plugin-docker-buildx, pushes to harbor.tail5b443a.ts.net/ldraney/prediction-assistant). Matches issue description of "minimal version" needing upgrade.
    • [x] Dockerfile -- verified: exists, correctly marked "don't touch" (multi-stage Ruby 3.4.9 build)
    • [x] Gemfile -- verified: exists, correctly marked "don't touch"

    Repo Placement

    OK. Issue filed on ldraney/prediction-assistant, file target is .woodpecker.yaml in the same repo. Single-repo change.

    Dependencies

    • #1696 "CI/CD pipeline -- Woodpecker config" (sprint:2, validation) -- predecessor, original CI setup already done
    • #1773 "Activate repo in Woodpecker + first green build" (sprint:10, backlog) -- downstream dependency, validates end-to-end pipeline after this ticket completes
    • #1767 "Add prediction-assistant to services terraform" (sprint:9, backlog) -- parallel infra work, may update image references in ArgoCD/Kustomize
    • No items in in_progress that block this ticket

    Acceptance Criteria

    6 criteria, all agent-verifiable by reading the resulting YAML:

    1. In-cluster clone step (alpine/git, Forgejo HTTP) -- check YAML step
    2. ruby-rails-build:latest base image -- check image field
    3. Test step: bin/rails test with Postgres service -- check commands and services
    4. kaniko build-and-push -- check kaniko step config
    5. In-cluster Harbor registry -- check registry URL
    6. Build-and-push only on main -- check when condition

    Test expectations: "pipeline runs green on first push after merge" is only verifiable post-deploy, which is appropriate for CI pipeline work and covered by downstream ticket #1773.

    Blast Radius

    Note: K8s deployment files reference the old image path that this ticket changes:

    • k8s/base/deployment.yaml -- 2 references to harbor.tail5b443a.ts.net/ldraney/prediction-assistant:latest
    • k8s/base/worker-deployment.yaml -- 1 reference to same

    After this ticket, CI pushes to prediction-assistant/app:${CI_COMMIT_SHA} via in-cluster Harbor. The k8s deployments still reference the old path. This is handled by downstream tickets (#1767 services terraform, #1773 activation) and is not a blocker for this ticket's scope.

    No other files in the repo reference CI-specific patterns (woodpecker, kaniko, ruby-rails-build). Blast radius is contained to the single file.

    Decomposition Assessment

    1 file target in 1 repo. 6 AC but all describe aspects of a single YAML file rewrite -- tightly coupled. Estimated agent time well under 5 minutes. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-ci-pipeline for the CI pipeline component. Non-blocking platform documentation gap.

    No other action needed. Ticket is READY for development.

  • Review: LIVE badge + pause button review-1778-2026-07-04

    Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sub-ticket of #90, split 2 of 2
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- As a user with active trading bots...
    • [x] Context -- explains LIVE badge vs active distinction, dependency chain
    • [x] File Targets -- 5 modify targets, 3 exclusions
    • [x] Feature Flag -- none (correct, this repo has no feature flag infra)
    • [x] Acceptance Criteria -- 4 criteria
    • [x] Test Expectations -- 5 test cases + run command
    • [x] Constraints -- Hotwire patterns, CSS consistency, field naming
    • [x] Checklist -- present
    • [x] Related -- lists parent #85, parent sub #90, deps #88/#89

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- found in project-prediction-assistant user-stories section (key: portfolio-builder, role: Trader (Lucas))
    • [x] arch:rails label -- Rails component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails for component rails (note: arch:rails is used across 20+ board items; this is a cross-cutting gap, not specific to this ticket)
    • [x] Forgejo issue -- #95, open

    File Targets

    • [x] app/views/bots/_bot_card.html.erb -- verified: exists, 41 lines, already has bot.active? conditional and status indicator patterns. Agent will add LIVE badge alongside existing status group.
    • [x] app/views/bots/show.html.erb -- verified: exists, ~800+ lines, already has status-badge--live CSS class and @bot.active? conditionals. Contains existing badge styling infrastructure the agent can extend.
    • [x] app/controllers/bots_controller.rb -- verified: exists, already has toggle action. Agent adds pause action alongside it.
    • [x] config/routes.rb -- verified: exists, no existing pause route. Agent adds route for pause action.
    • [x] test/controllers/bots_controller_test.rb -- verified: exists. Agent adds pause tests.
    • [x] app/services/order_service.rb (do-not-touch) -- verified: exists with dry_run enforcement, correctly excluded.
    • [x] app/models/strategy.rb (do-not-touch) -- verified: exists, correctly excluded. Note: trading_enabled field does NOT yet exist in schema -- depends on #88 migration.
    • [x] app/views/bots/_activation_panel.html.erb (do-not-touch) -- confirmed does not exist yet (handled in split 1, #94).

    Repo Placement

    OK -- issue filed on ldraney/prediction-assistant, all file targets are in prediction-assistant. Single-repo scope.

    Dependencies

    • #88 (Trading fields migration + Strategy model validations) -- board item #1764, backlog. Adds trading_enabled field to Strategy. CRITICAL: this ticket cannot be started until #88 is merged. The trading_enabled? method referenced throughout the AC does not yet exist in the codebase.
    • #89 (OrderService dry_run enforcement + budget guardrails) -- board item #1765, backlog. Adds enforcement layer. Required for the pause semantics to have meaning.
    • #94 (Activation panel + controller toggle action, split 1) -- board item #1777, backlog. Sibling split; provides the activate action this ticket's pause button complements.
    • All dependencies are documented in the issue body. All are in backlog -- ordering is correct (this ticket should be scheduled after its dependencies).

    Acceptance Criteria

    4 criteria, all testable by an agent:

    • LIVE badge on bot card -- verifiable via rendered HTML assertion
    • LIVE badge on detail page -- verifiable via rendered HTML assertion
    • Pause button visibility -- verifiable via conditional rendering test
    • Pause action behavior -- verifiable via controller test (trading_enabled: false, active: true)

    Test commands are real: bin/rails test test/controllers/bots_controller_test.rb. Coverage is adequate.

    Blast Radius

    Naming collision awareness needed: The existing show.html.erb already uses status-badge--live CSS class tied to @bot.active?, and BotPresenter#status_label already returns "Live" when active? is true. The new "LIVE" badge will be a separate concept tied to trading_enabled?. The ticket context explains this distinction (active = monitoring, trading_enabled = actual trading), but the implementing agent must take care not to conflate the two. No action needed -- the constraint is implicit in the design and the ticket context is clear enough.

    No sibling services affected. All changes are UI/controller-level in a single app.

    Decomposition Assessment

    5 file targets across 1 repo. 4 acceptance criteria. Estimated agent work: under 5 minutes (badge partial, controller action, route, tests). No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails for the Rails component. This is a cross-cutting gap affecting 20+ board items, not specific to this ticket. Does not block this ticket.

    No other action needed. Scope is solid.

  • Verdict: APPROVED

    Board item #1768 -- Add Keycloak realm + OIDC client. Forgejo issue: ldraney/pal-e-services#171. Previous review incorrectly flagged arch-keycloak as missing; it exists and is confirmed.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, references PR #63
    • [x] Repo -- ldraney/pal-e-services
    • [x] User Story -- present
    • [x] Context -- explains OmniAuth is merged but realm/client not provisioned
    • [x] File Targets -- terraform/k3s.tfvars (modify), terraform/keycloak.tf (do not touch)
    • [x] Feature Flag -- none (extra section, acceptable)
    • [x] Acceptance Criteria -- 4 criteria, all machine-verifiable
    • [x] Test Expectations -- tofu plan
    • [x] Constraints -- model after landscaping-assistant, localhost redirect, roles, refresh tokens
    • [x] Checklist -- present
    • [x] Related -- prediction-assistant project

    Traceability

    • [x] story:app-experience label -- App Experience story
    • [x] story note verified -- found in project-prediction-assistant user-stories section (links to story-prediction-assistant-app-experience)
    • [x] arch:keycloak label -- Keycloak architecture component
    • [x] arch note verified -- arch-keycloak note exists in pal-e-docs (id: 1728, title: "Architecture: Keycloak")
    • [x] Forgejo issue -- ldraney/pal-e-services#171, state: open

    File Targets

    • [x] terraform/k3s.tfvars -- verified: gitignored by convention (*.tfvars in .gitignore), k3s.tfvars.example is tracked and contains keycloak_realms (line 21) and keycloak_clients (line 123) maps with existing entries (landscaping-assistant at lines 232-256 as model)
    • [x] terraform/keycloak.tf -- verified exists (9520 bytes), correctly marked as do-not-touch (for_each loops handle everything)

    Repo Placement

    Correct. Keycloak realm/client config lives in pal-e-services terraform. Issue filed on pal-e-services. Single repo affected.

    Dependencies

    • Item #1767 "Add prediction-assistant to services terraform" (sprint:9, backlog) -- sibling infra ticket, independent. Keycloak config can be added before or after broader terraform onboarding.
    • Item #1693 "Keycloak realm, client, users, and login theme" (sprint:1, validation) -- historical predecessor for kalshi-assistant. Pattern already established.
    • No items in in_progress block this ticket.

    Acceptance Criteria

    All 4 criteria are machine-verifiable: realm entry check, client entry check, redirect URI check, tofu plan clean. Test command is real (cd terraform && tofu plan). No missing criteria.

    Blast Radius

    Small. Adding entries to existing HCL map variables (keycloak_realms, keycloak_clients). The for_each pattern in keycloak.tf handles provisioning. No downstream consumers affected -- this creates new resources, does not modify existing ones.

    Decomposition Assessment

    1 file target, 1 repo, 4 acceptance criteria. Estimated agent work well under 5 minutes. No decomposition needed.

    Recommendation

    No action needed. Scope is solid, traceability complete, file targets verified. Previous review verdict of NEEDS_REFINEMENT was incorrect -- the arch-keycloak note exists.

  • Verdict: APPROVED

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Standalone
    • [x] Repo — ldraney/pal-e-platform
    • [x] User Story — present
    • [x] Context — present
    • [x] File Targets — present (none to modify, correct for apply-only task)
    • [x] Feature Flag — none
    • [x] Acceptance Criteria — 4 criteria
    • [x] Test Expectations — present
    • [x] Constraints — present
    • [x] Checklist — present
    • [x] Related — present

    Traceability

    • [x] story:app-experience — App Experience
    • [x] story note verified — found in project-prediction-assistant user-stories section
    • [x] arch:edge-proxy — Edge Proxy
    • [x] arch note verified — arch-edge-proxy note exists in pal-e-docs (architecture note, active status, project my-vibes-world)
    • [x] Forgejo issue — ldraney/pal-e-platform#516, open

    File Targets

    • [x] No files to modify — correct, pillar config already exists
    • [x] salt/pillar/caddy.sls — verified: prediction-assistant entry at lines 21-23 (domain: prediction-assistant.com, proxy_target: prediction-assistant.tail5b443a.ts.net, www_redirect: true)
    • [x] salt/states/caddy/Caddyfile.j2 — verified: template exists, iterates pillar sites with reverse_proxy and optional www redirect

    Repo Placement

    OK — issue filed on ldraney/pal-e-platform, Salt states and pillar live in pal-e-platform. Single repo affected.

    Dependencies

    No blockers. No items currently in_progress on the board. Downstream: board item #1774 "Verify prediction-assistant.com is live" (backlog, sprint:10) depends on this ticket completing first. Item #1692 "DNS + reverse proxy — prediction-assistant.com" (validation) is the original DNS/proxy setup that created the pillar entry this ticket applies.

    Acceptance Criteria

    4 criteria, all concrete and agent-verifiable:

    • [x] Salt state applied — verifiable by running salt-ssh command (provided in Test Expectations)
    • [x] Caddy config includes site block — verifiable by inspecting generated Caddyfile on edge-proxy
    • [x] ACME cert obtained — verifiable; issue correctly notes may 502 until upstream is live
    • [x] curl TLS check — verifiable by running curl -I https://prediction-assistant.com

    Test command provided: salt-ssh edge-proxy state.apply caddy. Real and correct.

    Blast Radius

    Low. Running state.apply caddy regenerates the entire Caddyfile from all pillar entries (palinks, landscaping, prediction-assistant, westside, paldocs). This is idempotent for existing sites since their pillar entries are unchanged. The only new site block added will be prediction-assistant.com. No downstream consumers affected beyond this service.

    Decomposition Assessment

    0 file targets to modify, 4 acceptance criteria, estimated agent work well under 5 minutes (single salt-ssh command + verification curl). No decomposition needed.

    Recommendation

    No action needed.

  • Verdict: READY

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Standalone
    • [x] Repo — ldraney/pal-e-services
    • [x] User Story — present, proper As/I want/So that format
    • [x] Context — explains SQLite-to-Postgres migration history (Sprint 2, PR #33)
    • [x] File Targets — includes both modify and do-not-touch lists
    • [x] Feature Flag — none (appropriate for infra)
    • [x] Acceptance Criteria — 3 items, all verifiable
    • [x] Test Expectations — tofu plan command specified
    • [x] Constraints — follow existing pattern
    • [x] Checklist — standard 3-item checklist
    • [x] Related — prediction-assistant project referenced

    Traceability

    • [x] story:platform-setup label — Platform Setup
    • [x] story note verified — story-prediction-assistant-platform-setup found in project-prediction-assistant user-stories section
    • [x] arch:postgres label — PostgreSQL Architecture
    • [x] arch note verified — arch-postgres note exists in pal-e-docs (project: prediction-assistant, status: active)
    • [x] Forgejo issue — ldraney/pal-e-services#172, state: open

    File Targets

    • [x] terraform/k3s.tfvars — verified: exists (symlink to ../../secrets/pal-e-services/k3s.tfvars). Contains service_databases map at line 15 with existing entries for palinks and westside_basketball. Pattern is clear: map key = role name, value = {password, databases list}.
    • [x] terraform/databases.tf — verified: exists, contains for_each = var.service_databases loop that auto-provisions roles and databases. Correctly listed as do-not-touch.

    Database Name Verification

    Cross-checked against config/database.yml in prediction-assistant repo. Production config expects DATABASE_URL (primary), CACHE_DATABASE_URL, QUEUE_DATABASE_URL, CABLE_DATABASE_URL. The ticket's proposed databases (prediction_assistant, prediction_assistant_cache, prediction_assistant_queue, prediction_assistant_cable) match exactly.

    Repo Placement

    OK — issue filed on ldraney/pal-e-services, file target is in pal-e-services. Single-repo change.

    Dependencies

    • #1767 (id=1767) "Add prediction-assistant to services terraform" — sprint:9 sibling, independent (provisions namespace/Harbor/ArgoCD, not database)
    • #1768 (id=1768) "Add Keycloak realm + OIDC client" — sprint:9 sibling, independent
    • #1772 (id=1772) "tofu apply — provision prediction-assistant infra" — sprint:10, depends on this ticket being done first. Sprint ordering handles sequencing correctly.

    Dependencies are not explicitly documented in the issue body but the sprint ordering (9 before 10) handles sequencing. Acceptable for infra tickets in the same plan.

    Acceptance Criteria

    3 criteria, all agent-verifiable:

    • service_databases has prediction_assistant entry — verifiable by grep/read
    • 4 databases listed — verifiable by reading the databases list in tfvars
    • Password in gitignored tfvars — verifiable by checking file location (already a symlink to secrets/)

    Test command cd terraform && tofu plan is real and appropriate.

    Blast Radius

    Minimal. Adding a new entry to the service_databases map is purely additive. The for_each loop in databases.tf is stable and handles new entries without affecting existing databases (palinks, westside_basketball). No downstream consumers are affected by adding a new database role.

    Decomposition Assessment

    1 file target, 1 repo, 3 acceptance criteria. Estimated agent work: under 2 minutes (add a map entry with generated password). No decomposition needed.

    Recommendation

    No action needed.

  • Verdict: APPROVED

    Re-review of issue #82 after fix for dry_run status pattern. The issue body now correctly references status: "cancelled" with metadata: {"dry_run": true}, matching the actual OrderService#handle_dry_run implementation at app/services/order_service.rb:270-289.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Parent and dependency chain documented
    • [x] Repo — ldraney/prediction-assistant
    • [x] User Story — AI Portfolio Builder reference
    • [x] Context — Dry_run mode background, corrected status pattern
    • [x] File Targets — 7 files (4 new, 3 existing)
    • [x] Feature Flag — "none" (internal data model, acceptable)
    • [x] Acceptance Criteria — 6 criteria
    • [x] Test Expectations — Model, job, idempotency, scopes
    • [x] Constraints — Listed
    • [x] Checklist — Present
    • [x] Related — project-prediction-assistant

    Traceability

    • [x] story:portfolio-builder label — AI Portfolio Builder
    • [x] story note verified — found in project-prediction-assistant user-stories section
    • [x] arch:rails label — Rails architecture
    • [ ] arch note MISSING — [SCOPE] No arch-rails note exists in pal-e-docs. Low priority: arch:rails is used pervasively across all prediction-assistant tickets; creating a dedicated note is a project-wide task, not a blocker for this ticket.
    • [x] Forgejo issue — #82, open

    File Targets

    • [x] app/models/simulation_result.rb — NEW file, does not exist yet (correct)
    • [x] db/migrate/XXXXXX_create_simulation_results.rb — NEW migration (correct)
    • [x] app/jobs/simulation_aggregator_job.rb — NEW job (correct, existing jobs directory at app/jobs/ confirmed)
    • [x] app/models/strategy.rb — EXISTS, verified. Has has_many :trades and config validation framework. Adding simulated_pnl(period:) method is clean.
    • [x] test/models/simulation_result_test.rb — NEW test (correct)
    • [x] test/models/strategy_test.rb — EXISTS, verified. 12k file with existing test coverage.
    • [x] test/jobs/simulation_aggregator_job_test.rb — NEW test (correct)

    Critical Fix Verified

    The previous review found the issue assumed status: "dry_run" but OrderService uses status: "cancelled" with metadata: {"dry_run": true}. Verified against actual code:

    • OrderService#handle_dry_run (order_service.rb:270-289): trade.update!(status: "cancelled", metadata: trade.metadata.merge("dry_run" => true))
    • Trade::STATUSES = %w[pending filled cancelled settled] — "dry_run" is NOT a valid trade status, confirming "cancelled" is correct
    • Issue body AC, Context, and Constraints sections all consistently reference status: "cancelled" + metadata['dry_run'] == true

    Repo Placement

    OK — Issue filed on ldraney/prediction-assistant, all file targets are in the same repo.

    Dependencies

    • Depends on S6 bots (all 4 with dry_run support) — S6 is merged per project memory
    • Blocks #83 (Bot card performance indicators, board item #1759, in todo) and #84 (Bot detail P&L dashboard, board item #1760, in backlog)
    • Dependencies are documented in the Lineage section

    Acceptance Criteria

    6 ACs, all testable by an agent:

    • AC1: Model existence + schema — verifiable via migration and model file
    • AC2: Job aggregation from cancelled/dry_run trades — verifiable via test fixtures
    • AC3: Idempotency — verifiable by running job twice
    • AC4: Period scopes — verifiable via scope queries
    • AC5: Strategy convenience method — verifiable via unit test
    • AC6: Test coverage — meta-criterion, verified by presence of tests

    Note: The trades table has a pnl column (decimal) that the existing bots_controller already sums. The aggregation job can rely on this column for P&L computation. For dry_run trades where pnl may be nil, the agent will need to determine the simulation P&L mechanism — this is a reasonable implementation decision within scope.

    Blast Radius

    Minimal. SimulationResult is a new, standalone table. No existing code references it. The existing bots_controller.rb:13 sums Trade.pnl for display, but SimulationResult is a separate aggregation layer that does not modify Trade records. The recurring.yml will need an entry for SimulationAggregatorJob, following the existing MarketScannerJob pattern.

    Decomposition Assessment

    7 file targets / 1 repo / 6 ACs / ~5 min estimated. The AC count (6) is marginally over the >5 threshold, but the work is highly cohesive: model + migration + job + convenience method + tests for a single concept (simulation P&L aggregation). Splitting would create artificial dependency chains between tightly coupled artifacts. No decomposition needed.

    Recommendation

    No action needed — issue body is correct and complete after the dry_run status fix.

    [SCOPE] (non-blocking): Create architecture note arch-rails for the Rails architecture component. This applies to all prediction-assistant tickets with the arch:rails label, not just this one.

  • Verdict: APPROVED

    Board item #1764 — Forgejo issue ldraney/prediction-assistant#88. Sub-ticket of #85 (Go-live activation flow), split 1 of 3. First review.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Sub-ticket of #85
    • [x] Repo — ldraney/prediction-assistant
    • [x] User Story — present, well-formed
    • [x] Context — includes naming conflict rationale
    • [x] File Targets — 4 files (1 new, 3 existing) + explicit do-not-touch list
    • [x] Feature Flag — "none" with rationale (internal model change)
    • [x] Acceptance Criteria — 9 criteria
    • [x] Test Expectations — 7 unit tests + run command
    • [x] Constraints — 4 constraints including column naming rationale
    • [x] Checklist — standard 3-item
    • [x] Related — project, parent issue, user story doc

    Traceability

    • [x] story:portfolio-builder label — AI Portfolio Builder
    • [x] story note verified — found in project-prediction-assistant user-stories section
    • [x] arch:rails label — present on board item
    • [ ] arch note MISSING — [SCOPE] Create architecture note arch-rails for the Rails application component. This is a project-wide gap affecting all arch:rails tickets, not specific to this issue.
    • [x] Forgejo issue — #88, open

    File Targets

    • [x] db/migrate/YYYYMMDD_add_trading_fields_to_strategies.rb — new file; naming convention matches existing migrations (e.g. 20260704000001_create_price_patterns.rb)
    • [x] app/models/strategy.rb — verified: exists (82 lines), has base validations, config_value helper, and validation helpers for subclasses. Correct target for new trading validations.
    • [x] test/models/strategy_test.rb — verified: exists (335 lines), comprehensive test suite for STI, config validations. Correct target for new trading validation tests.
    • [x] db/schema.rb — verified: exists, strategies table at lines 76-85 currently has active, config, name, type columns. Auto-updated by migration.
    • [x] app/models/late_game_lock_config.rb — verified: max_concurrent_positions confirmed on line 10 as JSONB config key. Do-not-touch designation correct.

    Repo Placement

    OK — issue filed on ldraney/prediction-assistant, all file targets are in the same repo. Single-repo change.

    Dependencies

    • Sub-ticket of #85 (board item #1761, backlog). Split 1 of 3 — this is the data layer (migration + model), independent of the other splits (OrderService guardrails, Activation UI).
    • No blocking dependencies in in_progress or todo columns.
    • Sibling sub-tickets: #89 (OrderService dry_run enforcement, item #1765) and #90 (Activation UI panel, item #1766) — both in backlog, both depend on this ticket's columns existing.
    • Dependencies correctly documented in Lineage section.

    Acceptance Criteria

    9 AC, all agent-verifiable. Test command provided: bin/rails test test/models/strategy_test.rb. Each criterion is specific and testable.

    Minor observation: Only budget_cents has an explicit presence validation AC. The Constraints section says "Budget fields are nullable (only required when trading_enabled is true)" which could imply all three monetary/position fields need presence validations. However, the AC is intentionally asymmetric — max_bet_cents and trading_max_positions have numericality checks but no presence requirement, meaning they are optional even when trading is enabled. This is a reasonable design choice (budget is the hard constraint; bet size and position limits are optional tuning).

    Blast Radius

    • max_concurrent_positions is referenced in 6+ files as a JSONB config key (late_game_lock_bot.rb, position_tracker.rb, bot_catalog.rb, and multiple test files). The ticket correctly avoids this name by using trading_max_positions.
    • No existing code references trading_enabled, budget_cents, max_bet_cents, or trading_max_positions — clean namespace.
    • New columns are nullable (except trading_enabled default false). No existing Strategy records, tests, or fixtures will break.
    • BotPresenter, BotsController, and job classes reference Strategy but only query active, config, type, and name. No downstream breakage risk.

    Decomposition Assessment

    4 file targets in 1 repo — under the 3-files/2-repos threshold. 9 AC exceeds the >5 threshold, but the work is atomically cohesive: one migration adding 4 columns, one model adding conditional validations, one test file. Splitting would create artificial dependencies (migration in one PR, validations in another). Estimated agent time: ~3 minutes. 1 story point. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails in pal-e-docs for the Rails application component. This is a project-wide gap, not a blocker for this ticket.

    Scope is solid. All file targets verified. Naming conflict analysis confirmed against live code. Blast radius minimal. Ticket is ready for implementation.

  • Verdict: APPROVED

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sub-issue of #84, decomposition documented
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- complete As/I want/So that format
    • [x] Context -- clear background, references existing page structure
    • [x] File Targets -- 3 files (1 new, 2 modify) + explicit exclusions
    • [x] Feature Flag -- None (acceptable: internal UI enhancement, no feature flag doc in repo)
    • [x] Acceptance Criteria -- 5 verifiable criteria
    • [x] Test Expectations -- 3 tests + run command
    • [x] Constraints -- 4 constraints (inline SVG/CSS only, Turbo Frame patterns, no external JS, match styling)
    • [x] Checklist -- present
    • [x] Related -- 4 references (#84 parent, #82 dependency, #83 sibling, docs)

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- story-prediction-assistant-portfolio-builder exists in pal-e-docs (active user-story note)
    • [x] arch:rails label -- present on board item
    • [ ] arch note MISSING -- [SCOPE] No arch-rails note found in pal-e-docs. Systemic gap: arch:rails is used across 20+ board items but no backing note exists. Not specific to this ticket.
    • [x] Forgejo issue -- #86, open, valid URL https://forgejo.tail5b443a.ts.net/ldraney/prediction-assistant/issues/86

    File Targets

    • [x] app/views/bots/_performance_dashboard.html.erb -- NEW file. Correct directory verified: sibling partials exist (_activity_feed.html.erb, _bot_card.html.erb, _config_fields.html.erb, etc.)
    • [x] app/controllers/bots_controller.rb -- verified: exists (180 lines). show action at line 26 already loads strategy and trade data. Extension point is clear.
    • [x] test/controllers/bots_controller_test.rb -- verified: exists (20k). Test patterns established for show action.

    Repo Placement

    OK. Issue #86 filed on ldraney/prediction-assistant. All file targets are in this repo. Single-repo change.

    Dependencies

    • #82 (SimulationResult model + P&L aggregation service) -- DOCUMENTED dependency. Board item #1758, currently in backlog. SimulationResult does not exist in the codebase yet. This ticket must not move to in_progress until #82 is merged.
    • #84 (parent issue) -- in backlog. This ticket was decomposed from #84.
    • #83 (Bot card performance indicators) -- sibling feature, not a blocker.
    • #87 (Bot detail trade history table) -- sibling sub-issue, not a blocker.

    All dependencies are properly documented in the issue body and Related section.

    Acceptance Criteria

    All 5 criteria are verifiable by an agent:

    • AC1: Check rendered HTML for performance dashboard partial above activity feed -- testable via controller test
    • AC2: Check for inline SVG markup, absence of external script tags -- testable via response body inspection
    • AC3: Verify Turbo Frame markup and period param handling -- testable (existing Turbo Frame patterns in activity.html.erb provide reference)
    • AC4: Check response body for stat labels (total P&L, win rate, total trades, best day, worst day) -- testable
    • AC5: Test with no simulation data returns 200 with empty-state content -- explicitly covered in Test Expectations

    Test run command is valid: bin/rails test test/controllers/bots_controller_test.rb

    Blast Radius

    Low. The change adds a new partial rendered in show.html.erb (line 944 area, above the activity feed render). Does not modify existing partials or sibling concerns. The controller extension adds SimulationResult queries to show action. No downstream consumers affected. Activity feed and marketplace index are explicitly excluded from scope.

    Turbo Frame patterns are well-established in the codebase (activity.html.erb, _activity_feed.html.erb) providing clear reference for the period toggle implementation.

    Decomposition Assessment

    3 file targets in 1 repo, 5 acceptance criteria, estimated ~5 minutes agent work. The inline SVG chart is the most complex component but is bounded by the constraint of no external libraries. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails for the Rails application component. This is a systemic gap affecting 20+ board items, not specific to this ticket. Does not block approval.
  • Verdict: APPROVED

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sub-issue of #84, decomposition documented
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- As a/I want/So that format, clear
    • [x] Context -- Sufficient background, references existing show page layout and dependency
    • [x] File Targets -- 3 files (1 create, 2 modify), plus "NOT to touch" list
    • [x] Feature Flag -- "None" (appropriate for internal display feature)
    • [x] Acceptance Criteria -- 5 items, all testable
    • [x] Test Expectations -- 4 test cases + run command
    • [x] Constraints -- 4 constraints including Turbo Frame requirement and responsive design
    • [x] Checklist -- Present
    • [x] Related -- 4 related issues documented

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- found in project-prediction-assistant user-stories section (key: portfolio-builder, role: Trader (Lucas))
    • [x] arch:rails label -- present on board item
    • [ ] arch note MISSING -- [SCOPE] No arch-rails note found in pal-e-docs. This is systemic across many board items sharing this label. Not a blocker for this ticket.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/prediction-assistant/issues/87, open

    File Targets

    • [x] app/views/bots/_trade_history.html.erb -- new partial to create. Does not exist yet (correct).
    • [x] app/controllers/bots_controller.rb -- verified: exists, show action at line 26, already loads strategy and activity logs. Extension point is clear.
    • [x] test/controllers/bots_controller_test.rb -- verified: exists with 589 lines of existing tests covering index, show, update, toggle, activity. New trade history tests fit naturally.

    Repo Placement

    OK. Issue filed on ldraney/prediction-assistant, work targets the same repo. Single-repo scope.

    Dependencies

    • #82 (SimulationResult model + P&L aggregation service) -- Hard dependency, correctly documented in Context section. Board item #1758 is currently in backlog. SimulationResult model does not exist in the codebase yet. This ticket must not start until #82 is merged.
    • #84 (parent issue) -- Decomposed, this ticket is one of two sub-issues.
    • #86 (P&L chart + summary stats) -- Sibling sub-issue, independent scope. Correctly listed in "Files NOT to touch."
    • #83 (Bot card performance indicators) -- Related but independent.

    Acceptance Criteria

    5 acceptance criteria, all verifiable by automated tests:

    • Table rendering -- assert_select for partial presence
    • Column list -- assert_select for column headers
    • Pagination at 10 per page -- controller test with count assertion
    • Empty state message -- controller test with message text assertion
    • Turbo Frame pagination -- test response includes turbo-frame element

    Test expectations (4) align well with AC. Run command provided: bin/rails test test/controllers/bots_controller_test.rb.

    Blast Radius

    Low. The existing codebase already has a Turbo Frame pagination pattern in _activity_feed.html.erb (cursor-based with "Load older entries"). The new trade history adds a second pagination mechanism (offset/limit) on the same page. The ticket explicitly calls out using offset/limit rather than cursor, which is appropriate for a trade history table where users navigate forward and backward. No downstream consumers are affected. The change is additive -- new partial, minor controller extension.

    Decomposition Assessment

    3 file targets in 1 repo, 5 acceptance criteria, 4 test expectations. Estimated agent work well under 5 minutes. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails for the Rails application component. This is systemic across many board items and not specific to this ticket.

    No other action needed. Scope is solid, file targets verified, dependencies documented, acceptance criteria testable.

  • Verdict: APPROVED

    Ticket is well-scoped, file targets verified, traceability solid. One minor arch note gap flagged as systemic (not blocking).

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Sub-ticket of #85, split 2 of 3
    • [x] Repo — ldraney/prediction-assistant
    • [x] User Story — present, well-formed
    • [x] Context — explains relationship to #85, dependency on sub-ticket 1
    • [x] File Targets — 2 files to modify, 2 files explicitly excluded
    • [x] Feature Flag — "none" with justification (internal service change, no FF infra)
    • [x] Acceptance Criteria — 7 items
    • [x] Test Expectations — 5 unit tests + run command
    • [x] Constraints — backward compat, per-strategy budget, integer cents, log-not-raise
    • [x] Checklist — present
    • [x] Related — parent issue, dependency, user story doc

    Traceability

    • [x] story:portfolio-builder label — AI Portfolio Builder
    • [x] story note verified — found in project-prediction-assistant user-stories section (key: portfolio-builder, role: Trader)
    • [x] arch:rails label — present on board item
    • [ ] arch note MISSING — [SCOPE] No arch-rails note found in pal-e-docs. This is a project-wide gap: many prediction-assistant board items carry arch:rails but no backing note exists. Recommend creating arch-rails as a project-level architecture note covering the Rails 8.1 + Hotwire + Solid Queue stack.
    • [x] Forgejo issue — #89, open

    File Targets

    • [x] app/services/order_service.rb — verified: exists (14k), contains OrderService class with place_order, cancel_order, check_fill_status methods. Already has @dry_run pattern with handle_dry_run method. The bot: param is typed as Strategy, so bot.trading_enabled? and bot.budget_cents access path is valid once sub-ticket 1 adds those fields.
    • [x] test/services/order_service_test.rb — verified: exists (18k), established test file for OrderService
    • [x] NOT-TOUCH: app/models/strategy.rb — verified: currently has no trading_enabled or budget_cents fields (correct, those come from sub-ticket 1)
    • [x] NOT-TOUCH: Bot job files — verified: jobs (sweep_eligible_job.rb, pregame_threshold_job.rb) do not call OrderService directly; they delegate through bot service classes

    Repo Placement

    OK. Issue #89 filed on ldraney/prediction-assistant, all file targets are in that repo. Single-repo change.

    Dependencies

    • Depends on sub-ticket 1: issue #88 "Trading fields migration + Strategy model validations" (board item #1764, currently in backlog). This dependency is documented in the Lineage and Context sections. trading_enabled? and budget_cents must exist on Strategy before this ticket can be implemented.
    • No downstream blockers identified — this ticket is a prerequisite for sub-ticket 3 (Activation UI panel, #90).

    Acceptance Criteria

    7 ACs are well-defined and testable by an agent:

    • AC 1-2: trading_enabled? check + dry_run forcing — verifiable via unit test assertions on return value
    • AC 3-5: Budget enforcement — verifiable via unit tests with known trade amounts
    • AC 6: ActivityLog recording — verifiable by asserting ActivityLog.count change
    • AC 7: Backward compatibility — verifiable by running existing test suite unchanged

    Run command is valid: bin/rails test test/services/order_service_test.rb

    Blast Radius

    • Callers safe: PregameStackerBot and LateGameLockBot both create OrderService.new(client:, dry_run:) and pass bot: strategy to place_order. The new enforcement is internal to OrderService — callers need no changes.
    • cancel_order not affected: ACs scope enforcement to "placing any order" — cancellations are correctly excluded since the concern is preventing real money outflow.
    • No sibling service concern: OrderService is the single shared trade execution service; no similar pattern exists elsewhere that would need the same fix.

    Decomposition Assessment

    • File targets: 2 files in 1 repo — under the 3-file threshold
    • Acceptance criteria: 7 — over the 5-AC threshold on paper, but all are tightly coupled variations of one behavioral change (guard clause + budget check in a single method)
    • Estimated agent work: well under 5 minutes — add guard clause to place_order, add budget helper method, add ~5 test cases to existing test file
    • No decomposition needed

    Recommendation

    • [SCOPE] Create architecture note arch-rails covering the Rails 8.1 + Hotwire + Solid Queue stack. This is a project-wide gap affecting many board items, not specific to this ticket. Non-blocking for this review.
  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- present, documents parent (Paper trading dashboard S7) and dependency (SimulationResult model)
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- references AI Portfolio Builder
    • [x] Context -- clear motivation for adding performance badges
    • [x] File Targets -- 5 files listed
    • [x] Feature Flag -- none (enhancement to existing bot card)
    • [x] Acceptance Criteria -- 6 criteria
    • [x] Test Expectations -- unit, integration, N+1 described
    • [x] Constraints -- read-only display, dark-theme CSS compatibility
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- found in project-prediction-assistant user-stories section (key: portfolio-builder, role: Trader (Lucas))
    • [x] arch:rails label -- Rails architecture component
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (id: 2347, status: active). Note: search_notes does not surface this note but get_note with exact slug confirms it exists.
    • [x] Forgejo issue -- ldraney/prediction-assistant#83, state: open

    File Targets

    • [x] app/views/bots/_bot_card.html.erb -- verified: exists, contains bot card template with stats section (expected_return, trades, P&L). Adding a performance badge here is consistent.
    • [x] app/presenters/bot_presenter.rb -- verified: exists, BotPresenter class with pnl_display/pnl_css_class methods. Adding daily_pnl/weekly_pnl/pnl_trend follows established pattern.
    • [x] app/views/bots/index.html.erb -- verified: exists, renders _bot_card partial at line 270. Presenter wiring change may be needed here or in controller.
    • [x] test/presenters/bot_presenter_test.rb -- verified: exists, has comprehensive presenter tests for pnl formatting and status display.
    • [x] test/controllers/bots_controller_test.rb -- verified: exists, has integration tests for bot pages.

    Repo Placement

    OK -- issue filed on ldraney/prediction-assistant, all file targets are in the same repo. Single-repo change.

    Dependencies

    • SimulationResult model (issue #82, board item #1758) -- documented in Lineage section. Currently in backlog. The SimulationResult model does not yet exist in the codebase. This ticket cannot be implemented until #82 lands, but the dependency is properly documented. This is an ordering concern for sprint planning, not a scope defect.
    • No other blocking dependencies identified on the board.
    • This ticket blocks nothing (per Lineage).

    Acceptance Criteria

    6 criteria, all agent-verifiable:

    • AC 1-3: badge rendering (positive/negative/no-data/trend) -- verifiable via view tests
    • AC 4: presenter methods (daily_pnl, weekly_pnl, pnl_trend) -- verifiable via unit tests
    • AC 5: N+1 query prevention -- verifiable via query count assertion
    • AC 6: test coverage for all badge states -- meta-criterion, verifiable by running test suite

    Test expectations describe what to test (unit + integration + N+1). Standard Rails test runner (bin/rails test).

    Blast Radius

    • BotPresenter is used in: bots_controller.rb (3 instantiation sites), 4 config partials (comments only), _bot_card partial, index view.
    • _bot_card partial is only rendered from bots/index.html.erb -- no other consumers.
    • Changes are additive -- new methods on presenter, new HTML in partial. Existing functionality is not affected.
    • The presenter currently takes pnl_sum as a constructor argument. New daily/weekly P&L data will likely follow the same passive-data pattern (computed in controller, passed to presenter). No architectural change required.
    • No sibling services or downstream consumers affected.

    Decomposition Assessment

    5 file targets across 1 repo (under threshold). 6 acceptance criteria (technically >5 threshold, but all are tightly coupled to a single small feature -- adding a badge to a card). Estimated agent work: well under 5 minutes. No decomposition needed.

    Recommendation

    No action needed.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Parent: Paper trading dashboard (S7). Depends on: Bot detail P&L dashboard, Registration form (#58)
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- Links to docs/user-stories/ai-portfolio-builder.md (uses link style, not As/I want/So that format -- acceptable)
    • [x] Context -- Clear explanation of simulation vs real trading separation
    • [x] File Targets -- 6 files listed (4 existing, 2 new)
    • [x] Feature Flag -- none (core safety mechanism)
    • [x] Acceptance Criteria -- 8 items
    • [x] Test Expectations -- 5 expectations listed
    • [x] Constraints -- 3 constraints listed
    • [x] Checklist -- Standard 3-item checklist
    • [x] Related -- project-prediction-assistant

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- found in project-prediction-assistant user-stories section (Key: portfolio-builder, Role: Trader (Lucas))
    • [x] arch:rails label -- Rails Architecture
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (title: "Rails Architecture: Prediction Assistant", status: active)
    • [x] Forgejo issue -- ldraney/prediction-assistant#85, open

    File Targets

    • [x] app/models/strategy.rb -- verified: EXISTS. Currently has active boolean, no trading_enabled or budget fields. Correct modification target.
    • [x] db/migrate/XXXXXX_add_trading_fields_to_strategies.rb -- new migration, expected to not exist yet
    • [x] app/views/bots/_activation_panel.html.erb -- new partial, expected to not exist yet
    • [x] app/services/order_service.rb -- verified: EXISTS. Currently uses @dry_run constructor parameter. Ticket wants it to check strategy.trading_enabled? -- this is achievable since place_order already receives bot: (the strategy).
    • [x] test/models/strategy_test.rb -- verified: EXISTS, 320 lines of existing tests
    • [x] test/controllers/bots_controller_test.rb -- verified: EXISTS, 457 lines of existing tests

    Repo Placement

    OK -- issue filed on ldraney/prediction-assistant, all file targets are in the same repo. No cross-repo concerns.

    Dependencies

    • Registration form (#58, S6) -- board item 1729 is currently in todo column, not yet complete. Documented in Lineage.
    • Bot detail P&L dashboard -- dependency is documented but no specific issue number referenced. This should be clarified.
    • No in_progress items block this ticket directly.

    Acceptance Criteria

    8 ACs total. All are testable by an agent. However:

    • AC #4 (OrderService checks trading_enabled) is achievable -- place_order(bot: strategy, ...) already receives the strategy record.
    • AC #7 (Budget guardrail) requires summing existing trades against budget_cents -- straightforward but adds scope.
    • Naming conflict: AC #1 lists max_concurrent_positions as a new integer column on the strategies table. However, max_concurrent_positions already exists as a JSONB config key in LateGameLockConfig (app/models/late_game_lock_config.rb:10) and BotCatalog (app/models/bot_catalog.rb:214). Adding a same-named column creates ambiguity -- ActiveRecord will shadow the JSONB key with the column accessor.

    Blast Radius

    • OrderService consumers: PregameStackerBot (app/services/pregame_stacker_bot.rb) and BulkSweepJob (app/jobs/bulk_sweep_job.rb) both construct OrderService with dry_run: parameter. The ticket's change to check strategy.trading_enabled? inside OrderService would affect all callers -- they would get forced dry_run regardless of their constructor param. This is the intended behavior but must be tested across all consumers.
    • Toggle flow: The existing active toggle in BotsController (app/controllers/bots_controller.rb:74-85) and BotPresenter (app/presenters/bot_presenter.rb:19) use strategy.active?. The ticket introduces trading_enabled as a separate concept -- existing active remains for monitoring. No unintended collision.

    Decomposition Assessment

    • File targets: 6 files, 1 repo -- does NOT trigger the >3 files across >2 repos rule
    • Acceptance criteria: 8 items -- TRIGGERS the >5 AC rule
    • Estimated agent work: Model + migration + view partial + OrderService change + tests across 2 test files -- estimated 10-15 minutes, TRIGGERS the >5 minutes rule

    NEEDS DECOMPOSITION -- suggested split:

    1. Model + migration (3 AC): Add trading_enabled, budget_cents, max_bet_cents fields; migration; model validations and tests
    2. OrderService guard + budget enforcement (2 AC): trading_enabled? check forces dry_run; budget guardrail rejects over-budget orders
    3. Activation UI + controller flow (3 AC): Activation panel partial, LIVE badge, pause button, controller integration tests

    Recommendation

    • [BODY] Rename max_concurrent_positions column to avoid collision with existing JSONB config key in LateGameLockConfig. Suggested: trading_max_positions or move to JSONB config instead of a column.
    • [DECOMPOSE] 8 AC across 6 files, estimated >5 minutes. Route to skill-decompose-ticket with the three-way split above.
  • Verdict: READY

    Template Completeness

    • [x] Type (Feature)
    • [x] Lineage
    • [x] Repo
    • [x] User Story
    • [x] Context
    • [x] File Targets (create / modify / do-not-touch sections)
    • [x] Feature Flag (none)
    • [x] Acceptance Criteria (7 items)
    • [x] Test Expectations (5 items + run command)
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    Traceability

    • [x] story:watchdog-trading label -- Watchdog Trading
    • [x] story note verified -- found in project-prediction-assistant user-stories section
    • [x] arch:rails label -- Rails Architecture
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (id: 2347)
    • [x] Forgejo issue -- ldraney/prediction-assistant#40, open

    File Targets

    • [x] app/services/game_clock_estimator.rb -- to create, does not exist yet (correct)
    • [x] config/game_durations.yml -- to create, does not exist yet (correct)
    • [x] test/services/game_clock_estimator_test.rb -- to create, does not exist yet; test/services/ directory exists (correct)
    • [x] app/jobs/market_scanner_job.rb -- to modify, EXISTS; emit_time_update method at line 112 confirmed as integration point
    • [x] app/jobs/time_update_job.rb -- to modify, EXISTS; stub job with perform(ticker:, series_ticker:, event_start_time:, captured_at:) ready for extension
    • [x] test/jobs/market_scanner_job_test.rb -- to modify, EXISTS; comprehensive test suite with FakeKalshiClient pattern

    Repo Placement

    OK. Issue filed on ldraney/prediction-assistant, repo field matches, all file targets in this repo. Single-repo scope.

    Dependencies

    • Issue #4 (Late-Game Lock) -- primary consumer of enriched time data, currently in todo column (board item #1689). This ticket is a prerequisite for Late-Game Lock's time condition.
    • Issue #2 (MarketScanner) -- parent feature, in validation column (board item #1687). Scanner is the emission point being extended.
    • Issue #29 (Edge Learner) -- may use time data for pattern learning, in backlog column (board item #1716).
    • No blocking dependencies for this ticket -- it enriches scanner output using only existing data (series_ticker + event_start_time) without requiring other tickets first.

    Acceptance Criteria

    7 criteria, all testable by an agent. Test commands are valid: bin/rails test test/services/game_clock_estimator_test.rb test/jobs/market_scanner_job_test.rb. AC #7 (backward compatibility for existing TimeUpdateJob consumers) is important -- the agent must use optional kwargs with defaults when extending TimeUpdateJob#perform to avoid breaking existing scanner calls during implementation.

    Blast Radius

    Contained. TimeUpdateJob is currently a stub (# Stub -- bot consumers implement in Sprint 4) with no real downstream consumers. Only emitted by MarketScannerJob, which is one of the files being modified. No bot implementations need to change -- the ticket explicitly scopes bot-side interpretation to ticket #4 (Late-Game Lock). Adding new keyword arguments to TimeUpdateJob#perform requires backward-compatible defaults to satisfy AC #7.

    Decomposition Assessment

    6 file targets across 1 repo (does not trigger >3 files across >2 repos). 7 acceptance criteria (above the >5 threshold). However, all 7 ACs test behavioral facets of a single service (GameClockEstimator) and its integration point -- they are not independent deliverables. The work naturally splits into: (1) create GameClockEstimator + config + tests, (2) wire into scanner + extend TimeUpdateJob. These are tightly coupled -- you cannot test the estimator integration without both pieces. 3 story points is a modest estimate consistent with a single agent pass under 5 minutes. No decomposition needed.

    Recommendation

    No action needed.

  • Verdict: NEEDS_REFINEMENT

    Board item #1760 — Forgejo issue ldraney/prediction-assistant#84. Type: Feature.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Parent and dependencies identified
    • [x] Repo — ldraney/prediction-assistant
    • [x] User Story — AI Portfolio Builder
    • [x] Context — clear motivation
    • [x] File Targets — 5 files (3 new, 2 existing)
    • [x] Feature Flag — none (appropriate for core bot detail feature)
    • [x] Acceptance Criteria — 8 criteria
    • [x] Test Expectations — present
    • [x] Constraints — no external JS, Turbo Native compatible, dark theme
    • [x] Checklist — present
    • [x] Related — present

    Traceability

    • [x] story:portfolio-builder label — AI Portfolio Builder
    • [x] story note verified — found in project-prediction-assistant user-stories section
    • [x] arch:rails label — Rails Architecture
    • [x] arch note verified — arch-rails note exists in pal-e-docs (id: 2347, status: active)
    • [x] Forgejo issue — ldraney/prediction-assistant#84, state: open

    File Targets

    • [x] app/views/bots/show.html.erb — verified: exists (19k), renders bot detail page with P&L display via BotPresenter
    • [x] app/views/bots/_performance_dashboard.html.erb — new partial to create (no conflict, directory exists)
    • [x] app/views/bots/_trade_history.html.erb — new partial to create (no conflict, directory exists)
    • [x] app/controllers/bots_controller.rb — verified: exists, show action already loads strategy + pnl_sum via Trade queries
    • [x] test/controllers/bots_controller_test.rb — verified: exists (15k)

    Repo Placement

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

    Dependencies

    • SimulationResult model (issue #82, board item #1758) — in backlog. The ticket's Lineage says "Depends on: SimulationResult model (this sprint)" but #82 has no sprint label and is still in backlog. This is a real dependency: the P&L chart and trade history depend on simulated trade data. The current Trade model has no dry_run field; simulation tracking today is only via ActivityLog actions. #82 must be implemented before this ticket can be worked.
    • Activity feed (#30, board item #1717) — in todo. The issue says the dashboard goes "above the activity feed." If #30 isn't done yet, the show page layout reference point doesn't exist. However, the dashboard partial can still be added to show.html.erb at a logical position regardless.
    • Go-live activation flow (#85) — in backlog. This ticket blocks #85 per the Lineage. No action needed on this ticket's scope.

    Acceptance Criteria

    8 acceptance criteria. All are testable and verifiable by an agent:

    • Chart rendering — verifiable via HTML assertions on SVG/CSS elements
    • Period toggles — verifiable via Turbo Frame request/response tests
    • Summary stats — verifiable via presence of stat elements with computed values
    • Trade history table — verifiable via table row assertions
    • Pagination — verifiable via Turbo Frame page navigation
    • Empty state — verifiable with no trades in test fixture
    • Tests — meta-criterion, verifiable by test pass

    Test expectations describe what to test but do not specify exact test commands. This is acceptable since the repo uses standard Rails rails test.

    Blast Radius

    • BotPresenter already has pnl_display and pnl_css_class methods. This ticket extends the presenter, not replaces it. Low risk.
    • The bot index page also displays P&L (simple total). No changes needed there.
    • The "no external JS charting libraries" constraint aligns with the existing Propshaft + importmap architecture (no Node.js build step). Inline SVG/CSS is the right approach.
    • No similar P&L dashboard pattern exists elsewhere in the app to keep consistent with.

    Decomposition Assessment

    NEEDS DECOMPOSITION — route to skill-decompose-ticket.

    • File targets: 5 files in 1 repo — within single-repo threshold
    • Acceptance criteria: 8 ACs — exceeds the >5 threshold
    • Estimated agent work: SVG charting from scratch + Turbo Frame period toggles + trade history table + Turbo Frame pagination + 5 summary stats + empty state + tests across all — likely >5 minutes
    • Natural decomposition: (A) Performance chart + stats + period toggles (ACs 1-4), (B) Trade history table + pagination + empty state (ACs 5-7), with tests (AC 8) split across both

    Recommendation

    • [DECOMPOSE] 8 ACs across 2 functional areas. Split into: (A) P&L chart with summary stats and period toggles, (B) Trade history table with pagination and empty state. Route to skill-decompose-ticket.
  • Verdict: READY

    Template Completeness

    • [x] Type -- Bug
    • [x] Lineage -- Sprint 6 sub-ticket of #29, regression from #3
    • [x] Repo -- ldraney/prediction-assistant
    • [x] What Broke -- EdgeLearnerConfig validates max_group_size as positive integer, but spec defines it as enum
    • [x] Repro Steps -- 4 clear steps to reproduce
    • [x] Expected Behavior -- enum values should pass validation
    • [x] Environment -- file, line, commit, spec reference all provided
    • [x] Acceptance Criteria -- 5 criteria, all verifiable
    • [x] Related -- parent and introducing issue identified

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- found in project-prediction-assistant user-stories section
    • [x] arch:rails label -- Rails Architecture
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (architecture, active)
    • [x] Forgejo issue -- ldraney/prediction-assistant#72, open

    File Targets

    • [x] app/models/edge_learner_config.rb:24 -- verified: validate_positive_integer("max_group_size") is the bug
    • [x] docs/bots/edge-learner.md:72 -- verified: spec defines max_group_size as enum with values "pairs", "triples", "uncapped"
    • [x] app/models/bot_catalog.rb:248-249 -- verified: CONFIG_SCHEMAS has type: :number, default: 3 (needs updating to type: :select, default: "triples")
    • [x] test/models/strategy_test.rb:47 -- verified: "max_group_size" => 4 (integer, needs enum value)
    • [x] test/controllers/bots_controller_test.rb:53 -- verified: "max_group_size" => 3 (integer, needs enum value)
    • [x] test/controllers/bots_controller_test.rb:343 -- verified: max_group_size: "3" (string integer, needs enum value)
    • [x] test/services/position_tracker_test.rb:43 -- verified: "max_group_size" => 4 (integer, needs enum value)

    Repo Placement

    OK. Issue filed on ldraney/prediction-assistant, all file targets are in the same repo. Single-repo fix.

    Dependencies

    • Parent: #29 (Edge Learner bot, board item 1716, backlog) -- not blocking; this is an independent bug fix
    • Introduced by: #3 (EdgeLearnerConfig STI, board item 1688, validation) -- original source of the bug
    • No items on the board are blocked by this ticket
    • No undocumented dependencies found

    Acceptance Criteria

    5 criteria, all agent-verifiable:

    • AC1: Change validate_positive_integer to validate_inclusion -- verifiable by code inspection
    • AC2: Test enum acceptance/rejection -- verifiable by running test suite
    • AC3: BotCatalog CONFIG_SCHEMAS update -- verifiable by code inspection
    • AC4: Test helper updates across 3 test files -- verifiable by running test suite
    • AC5: Migration if schema change needed -- JSONB config column stores values as JSON; changing from integer to string values does not require a schema migration. This criterion is correctly conditional.

    Blast Radius

    • validate_positive_integer is used correctly in 4 other locations (BulkSweepConfig, LateGameLockConfig, StackerConfig, EdgeLearnerConfig's own min_simultaneous_markets) for fields that are genuinely integers. No same-bug pattern elsewhere.
    • All max_group_size references in test fixtures use integer values (3 or 4) that will need updating to enum strings -- the issue correctly identifies all 3 affected test files.
    • No downstream consumers of max_group_size outside the identified files. GroupBuilder service (mentioned in Related) is not yet implemented (part of #29).

    Decomposition Assessment

    No decomposition needed. 3 source files + 3 test files, all in one repo. 5 acceptance criteria (at boundary but straightforward). Estimated agent work well under 5 minutes -- this is a validation type swap with test fixture updates.

    Recommendation

    No action needed.

  • Verdict: READY

    Board item #1752 -- Forgejo issue ldraney/prediction-assistant#71. Re-review after issue update (issue updated 12:05 UTC, prior review at 10:32 UTC). Prior [BODY] findings largely resolved. All findings resolved. Prior arch-rails gap was a false positive (note exists at slug arch-rails, confirmed by multiple agents).

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sprint 6, sub-ticket of #29
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- "AI Portfolio Builder" (link reference format; matches story:portfolio-builder label). Prior review flagged "Bot Marketplace" mismatch -- now FIXED in current issue body.
    • [x] Context -- thorough; explains 3 event types, delegation targets, fan-out wiring needs with specific code references
    • [x] File Targets -- 5 targets (3 modify, 2 new)
    • [x] Feature Flag -- None (activation via EdgeLearnerConfig active toggle)
    • [x] Dependencies -- documented with issue refs and explicit ordering
    • [x] Merge Coordination -- clear merge order: PricePattern -> GroupBuilder -> EdgeLearnerJob, plus Late-Game Lock coordination
    • [x] Acceptance Criteria -- 5 items
    • [x] Test Expectations -- 5 specific test scenarios
    • [x] Constraints -- 4 constraints including concurrency and learning feedback
    • [x] Checklist -- standard 3-item
    • [x] Related -- parent and dependency references

    Traceability

    • [x] story:portfolio-builder label -- present on board item
    • [x] story note verified -- "portfolio-builder" found in project-prediction-assistant user-stories section (row: Trader (Lucas), links to story-prediction-assistant-portfolio-builder)
    • [x] arch:rails label -- present on board item
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (confirmed by multiple agents; prior "missing" finding was a false positive)
    • [x] Forgejo issue -- #71, open, valid URL

    File Targets

    • [x] app/jobs/edge_learner_job.rb -- verified: does not exist yet (new file). Reference pattern exists at app/jobs/pregame_threshold_job.rb (23 lines, delegates to bot service in perform method)
    • [x] app/jobs/price_update_job.rb -- verified: exists (21 lines), is a stub with empty perform body and comment "Stub -- bot consumers implement in Sprint 4". Header confirms consumers: "Late-Game Lock, Edge Learner"
    • [x] app/jobs/time_update_job.rb -- verified: exists (19 lines), is a stub with empty perform body and comment "Stub -- bot consumers implement in Sprint 4". Header confirms consumers: "Late-Game Lock, Edge Learner"
    • [x] app/jobs/sweep_eligible_job.rb -- verified: exists (27 lines), fans out to BulkSweepJob only, has comment "Edge Learner will be added in Sprint 4 (#29)". Fan-out pattern is established: BulkSweepJob.perform_later(...)
    • [x] test/jobs/edge_learner_job_test.rb -- verified: does not exist yet (new file)

    Repo Placement

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

    Dependencies

    • GroupBuilder service (#69) -- board item #1750, backlog column. app/services/group_builder.rb does not exist. Hard dependency for AC4. Issue documents this: "Depends on GroupBuilder service (sub-ticket)".
    • PricePattern model (#67) -- board item #1748, backlog column. app/models/price_pattern.rb does not exist. Hard dependency for Constraints ("Closed trade results must be fed back into PricePattern"). Issue documents this.
    • PositionTracker (#24) -- board item #1711, validation column. app/services/position_tracker.rb EXISTS. Required for AC3 exit logic.
    • OrderService (#23) -- board item #1710, validation column. app/services/order_service.rb EXISTS. Required for AC5.
    • EdgeLearnerConfig model -- EXISTS at app/models/edge_learner_config.rb. All referenced config keys verified: risk_level, bet_size_per_group, max_portfolio_exposure, stop_loss, take_profit, learning_sensitivity.
    • ActivityLog model -- EXISTS at app/models/activity_log.rb. Current ACTIONS: place_order, cancel_order, fill, partial_fill, error, dry_run, bulk_sweep. Agent will need to add Edge Learner-specific actions (group_generation, pattern_detection) during implementation.
    • Late-Game Lock -- board items #1747 and #1689, both in todo column. Also targets PriceUpdateJob and TimeUpdateJob. Issue correctly states: "Must merge AFTER Late-Game Lock PR to avoid Solid Queue job conflicts."

    Prior review concern resolved: Previous review recommended adding explicit ordering and merge coordination notes. These sections already exist in the issue body: Dependencies section states "Must merge AFTER Late-Game Lock" and Merge Coordination section gives explicit merge order "PricePattern -> GroupBuilder -> EdgeLearnerJob".

    Acceptance Criteria

    5 AC items, all testable with caveats:

    • AC1: Testable -- consumer pattern well-established from PregameThresholdJob
    • AC2: Testable -- mechanical fan-out additions (add EdgeLearnerJob.perform_later(...) to 3 jobs)
    • AC3: Testable -- complex but well-specified. 3 risk-level exit strategies (low=immediate, medium=trailing stop, high=take-profit). Requires PositionTracker (EXISTS) and OrderService (EXISTS)
    • AC4: Testable only after GroupBuilder (#69) merges -- delegates to GroupBuilder for group generation. Hard dependency documented in issue.
    • AC5: Testable -- standard ActivityLog pattern

    Minor gap: No AC covers specific time_update event handling behavior beyond AC1's "processes time_update payloads". Constraints mention "Learning model reversal signal overrides risk-level hold rules" but no AC explicitly tests this override.

    Blast Radius

    Low risk. All fan-out changes are additive:

    • PriceUpdateJob and TimeUpdateJob are stubs -- adding fan-out is their first real consumer wiring
    • SweepEligibleJob already fans out to BulkSweepJob -- adding EdgeLearnerJob follows the identical pattern
    • MarketScannerJob (the event source) is not modified -- it already emits all 3 event types
    • Late-Game Lock also targets PriceUpdateJob/TimeUpdateJob but merge order is documented

    Decomposition Assessment

    5 file targets in 1 repo, 5 acceptance criteria, follows established PregameThresholdJob consumer pattern. Fan-out changes are mechanical (3 one-line additions). The new job is the most complex piece but has clear AC and a reference implementation. No decomposition needed.

    Recommendation

    All checks pass. The sole prior blocker ([SCOPE] arch-rails note missing) was a false positive -- the note exists at slug arch-rails in pal-e-docs, confirmed by multiple agents. Prior [BODY] recommendations from first review were resolved in the current issue body. Ticket is ready for development.

  • Verdict: READY

    Board item #1748 -- Forgejo issue ldraney/prediction-assistant#67. Sub-ticket of #29 (Edge Learner bot). Re-review: all 3 prior issues resolved. Arch-rails false positive confirmed.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sprint 6, sub-ticket of #29
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- AI Portfolio Builder (FIXED from prior review: was Bot Marketplace, now correctly references AI Portfolio Builder)
    • [x] Context -- clear motivation for PricePattern as the Edge Learner learning layer
    • [x] File Targets -- 4 new files listed
    • [x] Feature Flag -- None, controlled by EdgeLearnerConfig active toggle
    • [x] Acceptance Criteria -- 5 criteria listed
    • [x] Test Expectations -- 4 items
    • [x] Constraints -- indexes and recalculation requirement noted
    • [x] Checklist -- standard 3-item checklist
    • [x] Related -- parent, dependency, consumer documented

    All required template sections present.

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- "portfolio-builder" row found in project-prediction-assistant user-stories section, links to story-prediction-assistant-portfolio-builder
    • [x] arch:rails label -- Rails component
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (confirmed by multiple agents; prior "missing" finding was a false positive due to search returning zero results despite the note existing at slug arch-rails)
    • [x] Forgejo issue -- ldraney/prediction-assistant#67, open

    File Targets

    • [x] db/migrate/YYYYMMDDHHMMSS_create_price_patterns.rb -- verified: no price_patterns table exists in db/schema.rb (schema version 2026_07_03_200002). Parent dir db/migrate/ exists with 6 existing migrations. New file.
    • [x] app/models/price_pattern.rb -- verified: no PricePattern model exists. Parent dir app/models/ exists with 14 models. New file.
    • [x] test/models/price_pattern_test.rb -- verified: does not exist. Parent dir test/models/ exists with 8 test files. New file.
    • [x] test/fixtures/price_patterns.yml -- verified: does not exist. Parent dir test/fixtures/ exists. New file.

    All four file targets are net-new. No references to PricePattern or price_pattern exist anywhere in the codebase (confirmed via grep across app/, test/, db/). Only existing mention of "price patterns" is in app/models/bot_catalog.rb marketing copy strings.

    Repo Placement

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

    Dependencies

    • Parent: #29 (Edge Learner bot) -- board item 1716, in backlog (Sprint 6). This ticket is a decomposed sub-ticket.
    • Depends on: #2 (MarketScanner data layer + Solid Queue event bus) -- prediction-assistant#2, closed/merged. Dependency satisfied.
    • Consumed by: EdgeLearnerJob consumer (future sub-ticket of #29) -- not yet a standalone board item. PricePattern is foundational; the consumer will use it once both are built.

    No blocking dependencies. PricePattern is a standalone data layer that can be built independently.

    Acceptance Criteria

    • AC 1 (migration columns) -- concrete and verifiable. Specifies all column names, types, and precision.
    • AC 2 (model validations) -- concrete and verifiable.
    • AC 3 (for_situation scope) -- concrete and verifiable. Clear query pattern with ordering.
    • AC 4 (confidence scoring) -- FIXED from prior review. Now specifies explicit formula: (win_count / sample_count) * log2(sample_count + 1) / log2(max_sample_count + 1), with win_count defined as patterns with exit_price > entry_price for the same (sport, game_situation) pair, capped at 1.0, minimum 5 samples before confidence exceeds 0.5. Minor ambiguity: max_sample_count is not explicitly defined (likely the max across all pairs, but could be a constant). Acceptable -- an agent can infer from context.
    • AC 5 (tests) -- concrete and verifiable.

    All 5 criteria are agent-verifiable. Prior vagueness in AC 4 has been resolved.

    Blast Radius

    Low. PricePattern is entirely new and self-contained. No existing code references it. No foreign keys to existing tables. The ticker and series_ticker columns match market_snapshots naming conventions (suggesting future integration), but this ticket introduces no coupling. Existing model patterns (MarketSnapshot validations, Strategy STI) provide clear Rails conventions to follow.

    Decomposition Assessment

    • 4 file targets in 1 repo -- under threshold
    • 5 acceptance criteria -- at limit but acceptable
    • All new files following standard Rails model + migration + test pattern
    • Estimated agent work: under 5 minutes

    No decomposition needed.

    Prior Review Issues -- Resolution Status

    • [x] [BODY] User Story reference fixed: now correctly says "AI Portfolio Builder" with link to docs/user-stories/ai-portfolio-builder.md
    • [x] [BODY] AC #4 confidence scoring clarified: explicit formula now provided with method name, calculation, and threshold
    • [x] [SCOPE] arch-rails note: FALSE POSITIVE -- the note exists at slug arch-rails in pal-e-docs. Prior review's search returned zero results, but multiple agents have confirmed the note exists. No action needed.

    Recommendation

    All checks pass. No [BODY], [LABEL], [SCOPE], or [DECOMPOSE] recommendations remain. The ticket is well-scoped and ready for implementation.

  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sprint 6, sub-ticket of #29
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- AI Portfolio Builder, Edge Learner group evaluation engine
    • [x] Context -- detailed background on Group Builder sub-system
    • [x] File Targets -- 2 new files listed
    • [x] Feature Flag -- None (internal service, appropriate)
    • [x] Acceptance Criteria -- 5 criteria
    • [x] Test Expectations -- 4 test areas
    • [x] Constraints -- 3 constraints listed
    • [x] Checklist -- standard 3-item checklist
    • [x] Related -- parent, consumer, config references

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- found in project-prediction-assistant user-stories section, links to story-prediction-assistant-portfolio-builder
    • [x] arch:rails label -- Rails component
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (false positive in prior review; confirmed by multiple agents)
    • [x] Forgejo issue -- #69, open

    File Targets

    • [x] app/services/group_builder.rb -- verified: new file, parent directory app/services/ exists with 5 existing services (kalshi_client.rb, order_service.rb, position_tracker.rb, pregame_stacker_bot.rb, sizing_engine.rb)
    • [x] test/services/group_builder_test.rb -- verified: new file, parent directory test/services/ exists with 5 existing test files

    Repo Placement

    OK -- issue filed on ldraney/prediction-assistant, all work scoped to the same repo. Single-repo change, no cross-repo concerns.

    Dependencies

    • Parent: #29 (Edge Learner bot) -- board item 1716, backlog, sprint:6
    • Consumed by: EdgeLearnerJob (separate sub-ticket of #29, not yet created or in backlog)
    • Config: EdgeLearnerConfig max_group_size field exists (line 7 of edge_learner_config.rb) -- but currently validated as positive integer. Issue spec says it should be enum ("pairs"/"triples"/"uncapped"). The issue acknowledges this in Related section: "fix tracked in separate bug sub-ticket". This is a known dependency, not a scoping failure.
    • No blocking items in in_progress column.
    • Pure logic service with zero database access -- no migration or schema dependency.

    Acceptance Criteria

    5 criteria, all verifiable by an agent:

    • AC1: Interface contract (GroupBuilder.new(markets, max_group_size:).call) -- testable with unit test
    • AC2: Exact group counts (3=7, 4=15, 5=31) -- testable with assertions
    • AC3: max_group_size filtering -- testable with enum boundary tests
    • AC4: Combined probability calculation -- testable with known inputs
    • AC5: Efficiency for 10 markets (1023 groups) -- testable with timing benchmark

    No missing criteria. Test expectations align with ACs. No test commands specified but the test file path is clear and follows existing conventions.

    Blast Radius

    Minimal. GroupBuilder is a new standalone service consumed only by EdgeLearnerJob (not yet built). No existing services reference GroupBuilder or use combinatorial logic. The only related pattern is sizing_engine.rb which uses "combination" in a different context (position sizing). No downstream consumers affected.

    Decomposition Assessment

    2 file targets, 1 repo, 5 acceptance criteria. Pure logic service with no external dependencies. Estimated agent work: under 5 minutes. No decomposition needed.

    Recommendation

    All checks pass. The prior NEEDS_REFINEMENT verdict was based on a false positive: the arch-rails architecture note does exist at slug arch-rails in pal-e-docs (confirmed by multiple agents). No remaining scope issues.

  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sub-ticket of #4, second of 3 from decomposition
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- present, well-formed
    • [x] Context -- thorough explanation of bot behavior and pattern references
    • [x] File Targets -- 3 files to modify/create, 3 files NOT to touch
    • [x] Feature Flag -- "none" with explanation (no flag infra, config controls activation)
    • [x] Acceptance Criteria -- 5 criteria, all testable
    • [x] Test Expectations -- unit tests, edge cases, dry-run, run command
    • [x] Constraints -- 4 constraints documented
    • [x] Checklist -- present
    • [x] Related -- project, parent issue, dependencies listed

    Traceability

    • [x] story:watchdog-trading label -- Watchdog Trading
    • [x] story note verified -- story-prediction-assistant-watchdog-trading exists (active, user-story type); entry found in project-prediction-assistant user-stories section
    • [x] arch:rails label -- Rails Architecture
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (active, architecture type)
    • [x] Forgejo issue -- ldraney/prediction-assistant#68, open

    File Targets

    • [x] app/services/late_game_lock_bot.rb -- new file (correct, to be created)
    • [x] app/services/late_game_lock_evaluator.rb -- file to modify; does not exist yet but depends on #66 (event dispatch sub-ticket, board item #1747 in todo column). Dependency correctly documented in Lineage. When #66 completes, this target will be valid.
    • [x] test/services/late_game_lock_bot_test.rb -- new file (correct, to be created)
    • [x] app/services/order_service.rb -- exists (14k, verified API: place_order(bot:, ticker:, side:, quantity:, price:))
    • [x] app/services/position_tracker.rb -- exists (11k, verified API: open_positions(user:, bot:), conflict?(user:, ticker:, side:, exclude_strategy:))
    • [x] app/models/late_game_lock_config.rb -- exists (1.1k, has bet_size and max_concurrent_positions in REQUIRED_CONFIG_KEYS)

    Repo Placement

    OK. Issue #68 is filed on ldraney/prediction-assistant and all file targets are in that same repo. Single repo, no cross-repo concerns.

    Dependencies

    • Blocking: Board item #1747 (issue #66, "Late-Game Lock: event dispatch wiring + dual-trigger evaluator") -- in todo column. Creates the LateGameLockEvaluator that this ticket modifies. Correctly documented in Lineage section.
    • Merged: Board item #1710 (#23, OrderService) -- in validation column. File exists and API verified.
    • Merged: Board item #1711 (#24, PositionTracker) -- in validation column. File exists and API verified.

    Acceptance Criteria

    5 ACs, all verified against actual codebase APIs:

    • [x] AC1: OrderService.place_order signature matches -- takes bot:, ticker:, side:, quantity:, price:
    • [x] AC2: PositionTracker.open_positions(bot: config) works -- user: defaults to nil
    • [x] AC3: config.trades.where(ticker:, status: %w[pending filled]).exists? matches PregameStackerBot#already_trading? pattern (line 132-134)
    • [x] AC4: PositionTracker.conflict?(ticker:, side:, exclude_strategy:) API matches -- user: defaults to nil
    • [x] AC5: Logging pattern Rails.logger.info("[LateGameLock] ...") consistent with PregameStackerBot convention

    All criteria are agent-verifiable via unit tests.

    Blast Radius

    Limited. New bot service following the established PregameStackerBot pattern. No changes to shared services (OrderService, PositionTracker). Error handling pattern (rescue DuplicateOrderError, OrderError) is consistent with existing consumers (PregameStackerBot in app/services/pregame_stacker_bot.rb lines 201-205, BulkSweepJob in app/jobs/bulk_sweep_job.rb lines 169-176). No downstream consumers affected.

    Decomposition Assessment

    3 file targets in 1 repo, 5 ACs, straightforward pattern-following implementation. Well under all thresholds (<3 files across >2 repos, <=5 ACs, estimated <5 min agent work). No decomposition needed.

    Recommendation

    No action needed.

  • Verdict: READY

    Re-review: Previous review was NEEDS_REFINEMENT due to missing arch-rails note. That note now exists (id 2347, status active). All checks pass.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sub-ticket of #4, third of 3 sub-tickets
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- As a trader, exit management for profit/loss
    • [x] Context -- Explains PositionTracker integration and auto_sell_vs_hold
    • [x] Architecture -- References arch-rails note (extra section, not required)
    • [x] File Targets -- 2 modify + 2 do-not-touch
    • [x] Feature Flag -- none (acceptable, no flag infra exists)
    • [x] Acceptance Criteria -- 5 criteria
    • [x] Test Expectations -- unit + edge cases + integration, run command provided
    • [x] Constraints -- 4 constraints
    • [x] Checklist -- PR, tests, no unrelated changes
    • [x] Related -- project, parent, dependencies

    Traceability

    • [x] story:watchdog-trading label -- Watchdog Trading
    • [x] story note verified -- found in project-prediction-assistant user-stories section (key: watchdog-trading, role: Trader)
    • [x] arch:rails label -- Rails architecture component
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (id 2347, note_type: architecture, status: active, title: "Rails Architecture: Prediction Assistant")
    • [x] Forgejo issue -- #70, open

    File Targets

    • [x] app/services/late_game_lock_bot.rb -- does not exist yet; correctly depends on #68 (entry logic) which will create this file. Issue says "extend" which is accurate given the sequencing dependency.
    • [x] test/services/late_game_lock_bot_test.rb -- does not exist yet; same dependency on #68. Correct sequencing.
    • [x] app/services/position_tracker.rb (NOT touch) -- verified: exit_candidates(bot:) at line 138, exit_signal at line 273, STOP_LOSS_THRESHOLD at line 32, TAKE_PROFIT_THRESHOLD at line 33 all present.
    • [x] app/services/order_service.rb (NOT touch) -- verified: place_order(bot:, ticker:, side:, ...) at line 72, accepts side: "sell".

    Repo Placement

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

    Dependencies

    • #4 (parent, board item 1689) -- in todo column
    • #66 (sibling, board item 1747) -- in todo: event dispatch wiring + dual-trigger evaluator. Transitive dependency.
    • #68 (sibling, board item 1749) -- in backlog: entry logic + OrderService integration. Direct dependency documented in Lineage. This ticket creates late_game_lock_bot.rb that #70 extends.
    • #24 PositionTracker (board item 1711) -- in validation (merged). Provides exit_candidates/exit_signal.
    • #23 OrderService (board item 1710) -- in validation (merged). Provides place_order.

    Dependency chain: #66 -> #68 -> #70. Predecessors correctly sequenced. Merged dependencies (#24, #23) confirmed in codebase.

    Acceptance Criteria

    5 criteria, all testable by an agent:

    1. process_exit calls PositionTracker.exit_candidates -- verifiable via unit test mock
    2. Stop-loss sell via OrderService -- verifiable
    3. Take-profit sell (auto_sell_vs_hold = "sell") -- verifiable
    4. Take-profit hold (auto_sell_vs_hold = "hold") -- verifiable, logs signal but no sell
    5. Idempotency: no duplicate sell orders -- verifiable via pending trade check

    Test expectations align with criteria. Run command: rails test test/services/late_game_lock_bot_test.rb.

    Blast Radius

    • No existing bot implements exit management via PositionTracker -- this is the first consumer of exit_candidates/exit_signal.
    • PregameStackerBot has zero exit logic. BulkSweepBot has no exit logic either.
    • Pattern established here will likely be reused by other bots, but scope is correctly limited to Late-Game Lock.
    • auto_sell_vs_hold config key validated in LateGameLockConfig (line 26), accepts "hold" or "sell" -- aligns with AC.

    Decomposition Assessment

    2 file targets in 1 repo, 5 acceptance criteria (at threshold), estimated agent time ~3-5 minutes. No decomposition needed.

    Recommendation

    No action needed.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Bug
    • [x] Lineage -- Sprint 6, sub-ticket of #29, pre-existing from #3
    • [x] Repo -- ldraney/prediction-assistant
    • [x] What Broke -- max_group_size validated as positive integer, should be enum
    • [x] Repro Steps -- 4-step reproduction path
    • [x] Expected Behavior -- string enum values should pass validation
    • [x] Environment -- file, line, commit, spec reference all present
    • [x] Acceptance Criteria -- 5 criteria listed
    • [x] Related -- parent, introduced-by, and downstream consumer identified

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- found in project-prediction-assistant user-stories section
    • [x] arch:rails label -- Rails component
    • [ ] arch note MISSING -- [SCOPE] search for "arch-rails" returned no results. Rails is the project's foundational framework; acceptable as foundational infrastructure if arch notes are only created for custom components.
    • [x] Forgejo issue -- ldraney/prediction-assistant#72, open

    File Targets

    • [x] app/models/edge_learner_config.rb, line 24 -- verified: validate_positive_integer("max_group_size") is exactly as described
    • [x] docs/bots/edge-learner.md, line 72 -- verified: max_group_size | enum | "triples" with values "pairs", "triples", "uncapped"
    • [x] app/models/strategy.rb, line 73 -- verified: validate_inclusion(key, allowed) method exists in base class, ready to use
    • [x] app/models/bot_catalog.rb, line 248 -- verified: max_group_size defined as type: :number, default: 3 in CONFIG_SCHEMAS (needs change to type: :select)

    Repo Placement

    OK -- issue filed on ldraney/prediction-assistant, all file targets are in the same repo. Single-repo fix.

    Dependencies

    • Parent ticket #29 (Edge Learner bot, board item #1716) is in backlog. This bug fix is independent and can proceed without the parent.
    • GroupBuilder service referenced in "Related" does not exist yet -- it is part of the parent feature. No downstream consumer to break.
    • No blocking dependencies. No items in in_progress that block this.

    Acceptance Criteria

    • [x] AC 1 -- Testable: swap validate_positive_integer for validate_inclusion with %w[pairs triples uncapped]
    • [x] AC 2 -- Testable: unit tests for valid/invalid values
    • [ ] AC 3 -- INACCURATE: says "BotCatalog::ENTRIES config_fields" but the config field definitions live in BotCatalog::CONFIG_SCHEMAS, not ENTRIES. The ENTRIES array holds display metadata (Entry structs). The actual change needed is at line 248 in CONFIG_SCHEMAS: change type: :number, default: 3 to type: :select, default: "triples" with options for pairs/triples/uncapped.
    • [ ] AC 4 -- INACCURATE: says "Test fixtures in test/fixtures/" but there are no EdgeLearner YAML fixture files. The test data with integer max_group_size values lives in test helper methods: test/models/strategy_test.rb:47 ("max_group_size" => 4), test/controllers/bots_controller_test.rb:53 ("max_group_size" => 3), and test/services/position_tracker_test.rb:43 ("max_group_size" => 4). These test files WILL need modification (changing integer values to enum strings), which contradicts "without modification".
    • [x] AC 5 -- Likely not needed: config is stored as JSON/JSONB, so string values work without schema migration. Correct to include as conditional.

    Blast Radius

    • validate_positive_integer is used in 5 other places across LateGameLockConfig, StackerConfig, and BulkSweepConfig -- all correct for their integer fields, no similar enum mismatch found.
    • max_group_size with integer values appears in 3 test files (strategy_test, bots_controller_test, position_tracker_test) -- all need updating to enum strings.
    • GroupBuilder service does not exist yet, so no runtime downstream breakage.
    • BotCatalog CONFIG_SCHEMAS renders the form field -- changing type from :number to :select will affect the bot detail page form rendering. The form rendering code should already handle :select type (used by risk_level, time_remaining_threshold, etc.).

    Decomposition Assessment

    3 source files + 3 test files in 1 repo. 5 AC (at threshold). Estimated agent time: under 5 minutes -- this is a mechanical validation swap, catalog field type change, and test data update. No decomposition needed.

    Recommendations

    • [BODY] AC 3: Change "BotCatalog::ENTRIES config_fields for the affected bot(s) are updated" to "BotCatalog::CONFIG_SCHEMAS entry for EdgeLearnerConfig is updated -- max_group_size field changes from type: :number, default: 3 to type: :select, default: 'triples' with options pairs/triples/uncapped"
    • [BODY] AC 4: Change "Test fixtures in test/fixtures/ are updated to reflect the config model changes -- all existing tests pass without modification" to "Test helper methods in strategy_test.rb, bots_controller_test.rb, and position_tracker_test.rb are updated to use enum string values ('triples' or 'pairs') instead of integers for max_group_size -- all tests pass"
    • [SCOPE] arch:rails note missing -- create architecture note arch-rails if the convention requires arch notes for framework-level technology choices, or document that foundational framework labels do not require backing notes.
  • Verdict: NEEDS_REFINEMENT

    Scope is solid and implementation-ready. Single issue: missing arch-rails backing note in pal-e-docs (systemic gap across all arch:rails board items).

    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

    Traceability

    • [x] story:watchdog-trading label -- Watchdog Trading story found in project-prediction-assistant user-stories section
    • [x] story note verified -- entry exists on project page with key, role, and success metric
    • [x] arch:rails label present
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails for the Rails component. This is a systemic gap affecting all arch:rails items on the board.
    • [x] Forgejo issue -- ldraney/prediction-assistant#68, open

    File Targets

    • [x] app/services/late_game_lock_bot.rb -- verified: does not exist yet, to be created (new bot service)
    • [x] app/services/late_game_lock_evaluator.rb -- verified: does not exist yet. Created by dependency #66 (event dispatch sub-ticket). Ticket correctly notes dependency in Lineage section: "Depends on the event dispatch sub-ticket (evaluator must exist to call the bot)."
    • [x] test/services/late_game_lock_bot_test.rb -- verified: does not exist yet, to be created
    • [x] app/services/order_service.rb (NOT to touch) -- verified exists. place_order, DuplicateOrderError, PermanentApiError, dry_run all confirmed present.
    • [x] app/services/position_tracker.rb (NOT to touch) -- verified exists. open_positions(bot:) and conflict?(ticker:, side:, exclude_strategy:) signatures confirmed.
    • [x] app/models/late_game_lock_config.rb (NOT to touch) -- verified exists. Inherits from Strategy (which has has_many :trades). All referenced config keys present: bet_size, max_concurrent_positions, price_threshold, stop_loss, take_profit, auto_sell_vs_hold.

    Repo Placement

    OK. Issue filed on ldraney/prediction-assistant, all file targets are in the same repo.

    Dependencies

    • #66 (event dispatch wiring + dual-trigger evaluator) -- BLOCKING dependency. Creates late_game_lock_evaluator.rb that this ticket modifies. Currently in backlog (item #1747). Must complete before this ticket can start. Documented in Lineage section.
    • #23 (OrderService) -- merged (in validation column). Provides place_order, error classes, dry-run support. Verified on disk.
    • #24 (PositionTracker) -- merged (in validation column). Provides open_positions, conflict?. Verified on disk.
    • #70 (exit management, 3rd sub-ticket) -- depends on THIS ticket. Will extend late_game_lock_bot.rb with process_exit method.
    • #4 (parent issue) -- in todo column. Decomposed into #66 -> #68 -> #70.

    Dependency chain is well-documented and correctly ordered.

    Acceptance Criteria

    5 ACs, all agent-verifiable:

    • AC1 (YES buy order): testable with mocked OrderService
    • AC2 (max_concurrent_positions): testable with PositionTracker mock returning varying counts
    • AC3 (dedup guard): testable -- exact pattern from PregameStackerBot#already_trading? confirmed: config.trades.where(ticker: ticker, status: %w[pending filled]).exists?
    • AC4 (conflict detection): testable with PositionTracker.conflict? mock
    • AC5 (logging): testable by asserting log output

    Test command provided: rails test test/services/late_game_lock_bot_test.rb

    Minor note: AC1 references config.bet_size but actual accessor pattern in codebase is config.config["bet_size"] (JSON column). Agent should follow PregameStackerBot pattern. Not blocking -- intent is clear.

    Blast Radius

    Low risk. This ticket creates new files (late_game_lock_bot.rb, tests) and modifies one file created by a dependency (late_game_lock_evaluator.rb). No changes to shared services. Follows identical patterns to PregameStackerBot -- same OrderService API, same PositionTracker API, same dedup logic. No downstream consumers affected beyond #70 which extends the same file.

    Decomposition Assessment

    • 3 file targets (2 new, 1 modify) across 1 repo -- within limit
    • 5 acceptance criteria -- at boundary, acceptable
    • Estimated agent work: ~3-4 minutes (follows well-established PregameStackerBot pattern)

    No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails for the Rails component in pal-e-docs. This is a systemic gap -- no arch-rails note exists, and the label is used across many board items. This is not a blocker for implementation but blocks full traceability compliance.
  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sub-ticket of #4, first of 3
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- As a trader, dual-trigger evaluation
    • [x] Context -- PriceUpdateJob/TimeUpdateJob stubs, pattern reference to PregameThresholdJob
    • [x] File Targets -- 6 files (2 modify, 4 create) + 2 files NOT to touch
    • [x] Feature Flag -- none (correct, no flag infra in codebase)
    • [x] Acceptance Criteria -- 5 criteria, all testable
    • [x] Test Expectations -- Unit and job tests, run command provided
    • [x] Constraints -- Queue, pattern, error handling, additivity
    • [x] Checklist -- PR, tests, no unrelated changes
    • [x] Related -- project, parent #4, #40, #29

    Traceability

    • [x] story:watchdog-trading label -- Watchdog Trading story
    • [x] story note verified -- found in project-prediction-assistant user-stories section (key: watchdog-trading, role: Trader)
    • [x] arch:rails label -- Rails architecture component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails for component Rails. Note: arch:rails is used across 15+ tickets on this board as foundational infrastructure. This is a project-level hygiene item, not specific to this ticket.
    • [x] Forgejo issue -- #66, open

    File Targets

    • [x] app/jobs/price_update_job.rb -- verified: exists, contains stub with correct parameters (ticker, series_ticker, yes_bid, yes_ask, last_price, previous_price), comment says "Consumers (Sprint 4): Late-Game Lock, Edge Learner"
    • [x] app/jobs/time_update_job.rb -- verified: exists, contains stub with correct parameters (ticker, series_ticker, event_start_time, captured_at), matching comment
    • [x] app/services/late_game_lock_evaluator.rb -- does not exist yet (to be created), correct
    • [x] test/jobs/price_update_job_test.rb -- does not exist yet (to be created), correct
    • [x] test/jobs/time_update_job_test.rb -- does not exist yet (to be created), correct
    • [x] test/services/late_game_lock_evaluator_test.rb -- does not exist yet (to be created), correct
    • [x] app/jobs/market_scanner_job.rb -- verified: NOT to touch, event emission already correct (emit_price_update, emit_time_update)
    • [x] app/models/late_game_lock_config.rb -- verified: NOT to touch, has time_remaining_threshold validation with presets [last_5_pct, last_10_pct, last_15_pct, last_quarter], sport_filters, price_threshold range 0.5-1.0

    Repo Placement

    OK -- Forgejo issue filed on ldraney/prediction-assistant, all file targets are in the same repo. Single-repo change.

    Dependencies

    • Parent #4 (board item 1689) -- "Late-Game Lock bot -- dual-trigger consumer" in todo column. This ticket is the first of 3 sub-tickets from decomposing #4.
    • #68 (board item 1749) -- "Late-Game Lock: entry logic + OrderService integration" in backlog. Downstream sibling: needs the evaluator from this ticket before entry logic can be wired.
    • #70 (board item 1751) -- "Late-Game Lock: exit management + PositionTracker integration" in backlog. Downstream sibling: depends on #68.
    • #29 (board item 1716) -- "Edge Learner bot" in backlog. Also consumes PriceUpdateJob/TimeUpdateJob in Sprint 6. Ticket correctly preserves Edge Learner stub comments.
    • #40 (board item 1723) -- "Sport-specific game clock integration" in backlog. Correctly identified as separate concern for real-time clock; this ticket uses elapsed-time ratio from event_start_time as a simpler proxy.

    No blockers: nothing in in_progress blocks this ticket. Dependency ordering is clear: #66 -> #68 -> #70.

    Acceptance Criteria

    5 criteria, all verifiable by an agent:

    • AC 1-2: Job dispatch to evaluator -- standard job test pattern, matches existing pregame_threshold_job_test.rb structure
    • AC 3: Dual-trigger logic -- unit-testable with mock config and event data. Note: the evaluator will need to query MarketSnapshot for the "other half" of the dual trigger (price data on time events, time data on price events). This is an implicit implementation detail but follows naturally from the existing MarketSnapshot model.
    • AC 4: Sport filter matching -- references PregameStackerBot#qualifies? pattern (verified: lines 85-103 of pregame_stacker_bot.rb). Pattern is clear and copyable.
    • AC 5: Fault tolerance -- rescue per-config, log and continue. Matches PregameStackerBot pattern (lines 48-54).

    Test command is valid: rails test test/services/late_game_lock_evaluator_test.rb test/jobs/price_update_job_test.rb test/jobs/time_update_job_test.rb

    Blast Radius

    • PriceUpdateJob and TimeUpdateJob are shared event jobs consumed by multiple bots. The ticket correctly preserves Edge Learner stub comments for Sprint 6 (#29).
    • MarketScannerJob emission is NOT changed -- the scanner already emits both PriceUpdateJob and TimeUpdateJob on every scan cycle.
    • SweepEligibleJob follows the same fan-out pattern (delegates to BulkSweepJob), confirming architectural consistency.
    • No other consumers of PriceUpdateJob/TimeUpdateJob exist currently beyond the stubs.

    Decomposition Assessment

    6 file targets in 1 repo, 5 acceptance criteria, estimated agent work under 5 minutes. The pattern is well-defined (follow PregameThresholdJob -> PregameStackerBot). No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-rails for the Rails component. This is a project-wide hygiene item affecting 15+ board items, not specific to this ticket. Does not block implementation.
  • Verdict: APPROVED

    Re-review of board item #1729 after refinement. Previous review review-1729-2026-07-03 found NEEDS_REFINEMENT with 7 [BODY] recommendations and 1 [SCOPE] recommendation. All 7 [BODY] items have been addressed in the updated issue.

    Template Completeness

    • [x] Type -- Feature
    • [x] User Story -- present
    • [x] Lineage -- present (Story: Landing Page, Sprint: 6)
    • [x] Repo -- ldraney/prediction-assistant
    • [x] Context -- present, now explicitly clarifies KalshiClient per-user integration is out of scope
    • [x] File Targets -- 8 targets, all verified (see below)
    • [x] Feature Flag -- "None" (acceptable)
    • [x] Acceptance Criteria -- 5 items (reduced from 6; credential revocation removed)
    • [x] Test Expectations -- 4 items including mailer test
    • [x] Constraints -- 5 items including Rails 8 encrypts requirement
    • [x] Checklist -- 7 items
    • [x] Related -- present

    Extra sections: ### Scope, ### Follow-ups (both helpful, not in template, harmless).

    Traceability

    • [x] story:landing-page label -- Landing Page & Registration
    • [x] story note verified -- landing-page entry found in project-prediction-assistant user-stories section
    • [x] arch:rails label -- present on board item
    • [ ] arch note MISSING -- [SCOPE] arch-rails note does not exist in pal-e-docs (carried over from previous review; project-level organizational task, not a scope blocker for this ticket)
    • [x] Forgejo issue -- #58, open

    File Targets

    • [x] app/models/user.rb -- verified: marked as (new), does not exist, correctly describes creating User model with Rails 8 encrypts. Previous review flagged this as incorrectly described; now fixed.
    • [x] db/migrate/YYYYMMDD_create_users.rb -- verified: marked as (new), correctly says "create users table". Previous review flagged "add columns" language; now fixed.
    • [x] app/controllers/registrations_controller.rb -- verified: marked as (new), does not exist
    • [x] app/views/registrations/new.html.erb -- verified: marked as (new), does not exist
    • [x] config/routes.rb -- verified: exists, currently has Keycloak auth routes and bot resources at lines 12-21
    • [x] app/mailers/user_mailer.rb -- verified: marked as (new), app/mailers/ directory does not exist. Previous review flagged this as missing; now added.
    • [x] app/views/user_mailer/welcome.html.erb -- verified: marked as (new), does not exist. Previous review flagged this as missing; now added.
    • [x] app/services/keycloak_admin_service.rb -- verified: marked as (new), does not exist. Previous review flagged this as missing; now added.

    Repo Placement

    OK -- all 8 file targets are in ldraney/prediction-assistant, matching the Forgejo issue. No cross-repo concern.

    Dependencies

    • Depends on #57 (Keycloak login flow) -- board item #1728, currently in validation column. Soft blocker but not blocking scope review.
    • watchdog_configs.user_id FK -- properly deferred to Follow-ups section (schema.rb line 98 has user_id column with no FK constraint).
    • No other blocking dependencies found on the board.

    Acceptance Criteria

    • 5 ACs, all testable and verifiable by an agent.
    • AC #6 (credential revocation) was removed per previous review recommendation and properly deferred to Follow-ups.
    • Scope items 5 (Keycloak user creation) and 6 (welcome email) are covered by Checklist items but not by ACs. Acceptable -- the ACs cover the user-facing behavior, and test expectations cover the mailer.

    Blast Radius

    Properly contained. The updated issue explicitly states that KalshiClient per-user integration is out of scope. The Follow-ups section documents the blast radius (kalshi_client.rb, order_service.rb, market_scanner_job.rb, and bot consumers) as a separate ticket. No undisclosed downstream effects.

    Decomposition Assessment

    8 file targets in 1 repo. 5 acceptance criteria. Scope covers: User model creation, migration, controller, view, routes, mailer, Keycloak admin service, credential validation. All targets are in a single repo and follow standard Rails conventions. With credential revocation and KalshiClient per-user integration removed, estimated agent work fits within 5 minutes. No decomposition needed.

    Previous Review Resolution

    All 7 [BODY] recommendations from review-1729-2026-07-03 verified as fixed:

    1. user.rb file target now correctly marked as (new) -- FIXED
    2. Migration description now says "create users table" -- FIXED
    3. Mailer file target added (user_mailer.rb + template) -- FIXED
    4. Keycloak admin service file target added -- FIXED
    5. Encryption approach pinned to Rails 8 encrypts macro -- FIXED
    6. AC #6 (credential revocation) removed, deferred to Follow-ups -- FIXED
    7. KalshiClient per-user integration explicitly out of scope in Context + Follow-ups -- FIXED

    1 [SCOPE] recommendation carried forward:

    • arch-rails architecture note still missing from pal-e-docs (project-level task, non-blocking)

    Recommendation

    • [SCOPE] Create architecture note arch-rails for component rails (carried over from previous review -- project-level organizational task, does not block implementation)

    No other action needed. Ticket is ready for implementation.

  • Verdict: APPROVED

    Re-review of review-1717-2026-07-03 (NEEDS_REFINEMENT). All four [BODY] fixes have been correctly applied. The issue spec now matches the actual codebase schema.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sprint 4, depends on #26 and bots running
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- Bot Marketplace
    • [x] Context -- detailed background with references
    • [x] File Targets -- 7 files listed (3 pre-existing, 4 to create)
    • [x] Feature Flag -- None (integral to UI, acceptable)
    • [x] Acceptance Criteria -- 10 criteria
    • [x] Test Expectations -- model, channel, system tests listed
    • [x] Constraints -- Action Cable, strategy scoping, append-only, throttling
    • [x] Checklist -- 11 items
    • [x] Related -- dependencies listed with issue refs

    All required sections present per template-issue-feature.

    Traceability

    • [x] story:bot-marketplace label -- Bot Marketplace user story
    • [x] story note verified -- found in project-prediction-assistant user-stories section (key: bot-marketplace, links to story-prediction-assistant-bot-marketplace)
    • [x] arch:frontend label -- frontend component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-frontend for component frontend (carried forward from review-1717-2026-07-03; non-blocking for issue spec)
    • [x] Forgejo issue -- ldraney/prediction-assistant#30, open

    File Targets

    • [x] app/models/activity_log.rb -- EXISTS, schema now matches AC #1 exactly (strategy_id, bot_name, ticker, action, side, price, quantity, status, message, metadata)
    • [x] db/migrate/20260703200001_create_activity_logs.rb -- EXISTS
    • [x] app/channels/bot_activity_channel.rb -- does not exist yet, to be created (correct)
    • [x] app/views/bots/_activity_feed.html.erb -- does not exist yet, to be created (correct)
    • [x] app/views/activity_logs/_activity_log.html.erb -- does not exist yet, to be created (correct)
    • [x] test/models/activity_log_test.rb -- EXISTS with 14 tests covering validations, scopes (for_bot, recent, successes, failures), associations, metadata defaults
    • [x] test/channels/bot_activity_channel_test.rb -- does not exist yet, to be created (correct)

    Previous Review Fix Verification

    All four [BODY] recommendations from review-1717-2026-07-03 verified as correctly applied:

    • [x] AC #1 fields updated -- now lists strategy_id, bot_name (not bot_type), correct action enum values, message (not rationale), metadata (jsonb). No user_id or timestamp. Matches db/schema.rb and activity_log.rb model constants exactly.
    • [x] AC #6 scoping updated -- now says "scoped to strategy_id (user-level scoping deferred until User model lands)". No user_id reference.
    • [x] AC #8 field name updated -- display list now says "message" not "rationale".
    • [x] AC #9 reframed -- prefixed with "Pre-existing:" and reworded as "verify integration and ensure all bots write correctly".

    Repo Placement

    OK -- issue filed on ldraney/prediction-assistant, all file targets in that repo. Single-repo scope.

    Dependencies

    • #26 (Bot Detail pages) -- validation column, merged. app/views/bots/show.html.erb exists on disk. SATISFIED.
    • #23 (OrderService) -- validation column. OrderService confirmed to call ActivityLog.create! in app/services/order_service.rb. SATISFIED.
    • #27 (Pregame Stacker) -- validation column, merged. SATISFIED.
    • #28 (Bulk Sweep) -- validation column, merged. SATISFIED.
    • #29 (Edge Learner) -- in backlog (sprint:6). NOT blocking -- feed works with whatever bots exist.
    • #48 (Hotwire conversion) -- validation column. Action Cable configured: config/cable.yml uses solid_cable adapter, application.rb requires action_cable/engine, application_cable/ directory exists with channel.rb and connection.rb. SATISFIED.

    Acceptance Criteria

    • AC #1 (model fields) -- now matches actual schema. Verified against db/schema.rb and model ACTIONS/STATUSES constants.
    • AC #2 (Kalshi market link) -- testable, straightforward
    • AC #3 (feed partial on detail page) -- testable, show.html.erb exists to embed it
    • AC #4 (per-bot filtering) -- testable, for_bot scope exists and tested
    • AC #5 (Turbo Streams real-time) -- testable, Action Cable + solid_cable fully configured
    • AC #6 (channel scoped to strategy_id) -- correctly scoped, user_id deferred
    • AC #7 (pagination) -- testable
    • AC #8 (entry display fields) -- now uses correct field names (message, not rationale)
    • AC #9 (pre-existing integration) -- correctly reframed as verification of existing OrderService integration
    • AC #10 (reverse chronological) -- testable, recent scope exists and tested

    All criteria are accurate and agent-verifiable.

    Blast Radius

    • OrderService already creates ActivityLog entries -- adding after_create broadcast will fire on every trade execution across all bots
    • BulkSweepJob processes many markets rapidly -- broadcasting per-entry could flood the cable. Constraints section mentions throttling but no specific mechanism prescribed (acceptable at 2pt scope -- agent can choose debounce/batch strategy)
    • Action Cable uses solid_cable (database-backed pub/sub) -- performance under high-frequency broadcasting should be monitored
    • No cross-user leakage concern currently since there is no multi-user model yet

    Decomposition Assessment

    7 file targets (3 already exist), 10 acceptance criteria (2 pre-existing, effective count ~8). 1 repo. Remaining work is 4 new files + wiring. Standard Rails patterns (channel, partials, Turbo Streams). 2 story points. No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-frontend for component frontend (carried forward from previous review; non-blocking -- "frontend" is a broad architectural label, and the issue spec is self-contained and implementable without it).

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

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- "Feature (bot consumer -- Solid Queue job)"
    • [x] Lineage -- Present, references Sprint 4 dependencies
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- Present (references AI-Powered Portfolio Builder)
    • [x] Context -- Detailed, explains dual-trigger architecture
    • [x] File Targets -- 4 files + tests listed
    • [ ] Feature Flag -- Name only (late_game_lock_enabled). Missing required fields: Type, Default, Visibility, Removal. Also, no feature flag infrastructure exists in the codebase.
    • [x] Acceptance Criteria -- 17 items (excessive, see Decomposition)
    • [x] Test Expectations -- Present with unit, integration, and edge cases
    • [x] Constraints -- Present
    • [x] Checklist -- Present
    • [x] Related -- Present

    Traceability

    • [x] story:watchdog-trading label -- Watchdog Trading story
    • [x] story note verified -- watchdog-trading found in project-prediction-assistant user-stories section
    • [ ] User story body mismatch -- Issue body references "AI-Powered Portfolio Builder" (portfolio-builder) but label is story:watchdog-trading. The label is correct (Late-Game Lock targets 85%+ win rate), but the issue body should reference the matching story. [BODY]
    • [x] arch:rails label -- Rails component
    • [ ] arch note MISSING -- No arch-rails note found in pal-e-docs. [SCOPE] Create architecture note arch-rails for Rails component.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/prediction-assistant/issues/4, open. Redirects from old kalshi-assistant URL.

    File Targets

    • [ ] app/jobs/late_game_lock_job.rb -- ISSUE: Issue says this should be on the late_game_lock queue, but existing architecture has PriceUpdateJob and TimeUpdateJob as stubs on the events queue waiting for Sprint 4 consumers (their comments say "Consumers (Sprint 4): Late-Game Lock, Edge Learner"). The Late-Game Lock should be called FROM these event jobs, not subscribe to its own queue. Queue architecture needs reconciliation. [BODY]
    • [ ] app/services/late_game_lock_evaluator.rb -- New file, reasonable scope for dual-trigger logic.
    • [ ] app/services/late_game_lock_exit_checker.rb -- ISSUE: PositionTracker (app/services/position_tracker.rb) already has exit_candidates and exit_signal methods that evaluate stop_loss and take_profit thresholds. Creating a separate LateGameLockExitChecker would duplicate this logic. Issue should clarify whether to extend PositionTracker or replace its exit logic. [BODY]
    • [ ] config/late_game_lock_sport_mappings.yml -- ISSUE: LateGameLockConfig model already validates time_remaining_threshold against a hardcoded list: %w[last_5_pct last_10_pct last_15_pct last_quarter]. But the issue specifies sport-specific mappings (NFL 4th quarter <5min, MLB 9th inning, etc.) which are a different concept than generic time percentages. The YAML config and the model validation need reconciliation. [BODY]

    Repo Placement

    OK. Issue specifies ldraney/prediction-assistant, which matches the current repo name. The board item Forgejo URL still uses the old kalshi-assistant name but redirects correctly.

    Dependencies

    • #2 MarketScanner + event bus -- board item 1687, in validation column
    • #3 Strategy STI schema -- board item 1688, in validation column
    • OrderService -- board item 1710, in validation column
    • PositionTracker -- board item 1711, in validation column

    All 4 dependencies are in the validation column (not yet done). If validation surfaces issues in any dependency, this ticket's implementation may need to change. The dependencies themselves exist in the codebase and are functional: MarketScannerJob emits time_update and price_update events, OrderService handles trade execution, PositionTracker handles position monitoring including exit signals, and LateGameLockConfig STI model is already implemented.

    Acceptance Criteria

    17 acceptance criteria. All are individually testable. However:

    • Missing AC for late_game_lock_enabled feature flag behavior (what happens when disabled?)
    • Missing AC for activity logging (OrderService logs trades, but bot-level event processing logs are not specified)
    • Missing AC for error handling when dependencies (OrderService, PositionTracker) are unavailable
    • AC #1 says "on the late_game_lock queue" which conflicts with the stub-based event dispatch pattern in PriceUpdateJob/TimeUpdateJob
    • AC #11-14 (exit checker) overlap with PositionTracker's existing exit_candidates/exit_signal methods

    Blast Radius

    • PriceUpdateJob (app/jobs/price_update_job.rb) -- Currently a stub. Implementing Late-Game Lock requires adding consumer dispatch logic here. Edge Learner (Sprint 6) will also consume from this job. Changes must be additive.
    • TimeUpdateJob (app/jobs/time_update_job.rb) -- Currently a stub. Same concern as PriceUpdateJob.
    • PositionTracker (app/services/position_tracker.rb) -- Already has exit_candidates and exit_signal logic. Creating a separate LateGameLockExitChecker risks duplication or inconsistency.
    • LateGameLockConfig (app/models/late_game_lock_config.rb) -- Already validates time_remaining_threshold against hardcoded values. YAML sport mappings would need to align with or replace this validation.
    • Other bots (PregameStacker, BulkSweep) are not affected -- they use different event paths.

    Decomposition Assessment

    NEEDS DECOMPOSITION -- 17 ACs across 4+ new files + test files + modifications to 2 existing stub jobs. Estimated agent work exceeds 5 minutes significantly.

    Recommended decomposition into 3 sub-tickets:

    1. Event dispatch wiring + dual-trigger evaluator (ACs 1-6, 9): Wire PriceUpdateJob and TimeUpdateJob stubs to dispatch to Late-Game Lock. Create LateGameLockEvaluator with sport-specific time mapping and dual-trigger logic. ~5 pts.
    2. Entry logic + OrderService integration (ACs 7-8, 10, 16-17): Flat bet sizing, config reading, OrderService calls, idempotency. ~3 pts.
    3. Exit management + PositionTracker integration (ACs 11-15): Reconcile with existing PositionTracker exit logic. Stop-loss, take-profit, auto_sell_vs_hold. ~3 pts.

    Recommendation

    • [LABEL] Update board item title from "Watchdog -- auto-buy when market crosses 85% threshold" to "Late-Game Lock bot -- dual-trigger consumer (price + time)" -- the issue explicitly says "This is NOT the watchdog/scanner."
    • [BODY] Fix queue architecture: Replace "late_game_lock queue" with dispatch from PriceUpdateJob/TimeUpdateJob stubs. Both stubs already have comments saying "Consumers (Sprint 4): Late-Game Lock, Edge Learner".
    • [BODY] Reconcile exit checker with PositionTracker: Either extend PositionTracker.exit_candidates (which already handles stop_loss/take_profit) or document why a separate LateGameLockExitChecker is needed.
    • [BODY] Reconcile sport mappings YAML with LateGameLockConfig validation: The model validates time_remaining_threshold against generic presets (last_5_pct, last_10_pct, etc.) but the issue specifies sport-specific mappings (NFL 4th quarter <5min, MLB 9th inning, etc.). These are different concepts.
    • [BODY] Complete Feature Flag section with Type, Default, Visibility, and Removal fields per template-issue-feature. Note: no feature flag infrastructure exists in the codebase yet.
    • [BODY] Fix user story reference to match label: body says "AI-Powered Portfolio Builder" but label is story:watchdog-trading ("Watchdog Trading").
    • [SCOPE] Create architecture note arch-rails for the Rails component.
    • [DECOMPOSE] 17 ACs across 4+ new files + 2 stub modifications exceeds the 5-minute rule. Route to skill-decompose-ticket with the 3-way split recommended above.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — present
    • [x] Repo — ldraney/prediction-assistant
    • [x] User Story — present
    • [x] Context — present
    • [x] File Targets — present (but inaccurate, see below)
    • [x] Feature Flag — "None" (acceptable for this ticket)
    • [x] Acceptance Criteria — 6 items present
    • [x] Test Expectations — present
    • [x] Constraints — present
    • [x] Checklist — present
    • [x] Related — present

    Extra section: ### Scope (not in template, harmless).

    Traceability

    • [x] story:landing-page label — Landing Page & Registration
    • [x] story note verified — story-prediction-assistant-landing-page exists in pal-e-docs and in project-prediction-assistant user-stories section
    • [x] arch:rails label — present on board item
    • [ ] arch note MISSING — [SCOPE] Create architecture note arch-rails
    • [x] Forgejo issue — #58, open

    File Targets

    • [ ] app/models/user.rb — ISSUE: file does not exist. Issue says "add encrypted credential fields" implying modification, but user.rb and the users table need to be created from scratch. No User model or users table exists in the current schema.
    • [ ] db/migrate/ — ISSUE: description says "add credential columns" but there is no users table in the schema. Should say "create users table with credential columns."
    • [x] app/controllers/registrations_controller.rb — verified: marked as new, correctly does not exist yet
    • [x] app/views/registrations/new.html.erb — verified: marked as new, correctly does not exist yet
    • [x] config/routes.rb — verified: exists, currently has Keycloak auth routes and bot resources

    Missing file targets:

    • No mailer listed — but Scope item 5 says "Send welcome email with generated password." Needs app/mailers/ target.
    • No Keycloak admin API integration — but Scope item 4 says "Create Keycloak user account on successful validation." Needs a service or initializer for Keycloak Admin REST API calls (and likely keycloak-admin gem in Gemfile).
    • No encryption gem specified — Gemfile currently has no encryption library (lockbox, attr_encrypted, etc.). Issue should specify whether to use Rails 8 encrypted attributes, lockbox, or another approach, and add the gem to Gemfile.
    • No credential management UI — but AC #6 says "Revoking API access (deleting credentials) is possible." Needs controller action and possibly a view for credential deletion.

    Repo Placement

    OK — all file targets are in ldraney/prediction-assistant. Keycloak admin API calls are made from the Rails app, so no cross-repo concern, though Keycloak realm configuration (client permissions for admin API) may need a separate infra ticket if not already set up.

    Dependencies

    • Explicitly depends on #57 (Keycloak login flow) — board item #1728, currently in validation column. Implemented but not yet validated. Soft blocker.
    • No other blocking dependencies found on the board.
    • watchdog_configs table already has a user_id column (schema.rb line 98) but with no foreign key to a users table — this ticket will need to address that relationship.

    Acceptance Criteria

    • AC #1–#5 are testable and clear.
    • AC #6 "Revoking API access (deleting credentials) is possible" — introduces credential management functionality that is not mentioned in the Scope or File Targets sections. Should either be removed (separate ticket) or explicitly scoped with file targets.
    • Missing AC for Keycloak user creation (Scope item 4) and welcome email delivery (Scope item 5).

    Blast Radius

    Per-user credential transition: KalshiClient (app/services/kalshi_client.rb) currently reads credentials from Rails.application.config.kalshi (global singleton). Moving to per-user credentials will require changes in how KalshiClient is instantiated across at least 4 files:

    • app/services/order_service.rb
    • app/jobs/market_scanner_job.rb
    • app/services/pregame_stacker_bot.rb
    • app/jobs/bulk_sweep_job.rb

    None of these are in the file targets. The issue should clarify whether this ticket wires per-user credentials into the service layer or if that integration is a separate ticket.

    Decomposition Assessment

    5 listed file targets (really 8+ when missing targets are added). 6 acceptance criteria. 6 scope items spanning: User model creation, migration, controller, view, Keycloak admin API integration, email sending, encryption setup, and credential validation. Estimated agent work exceeds 5 minutes. However, if scope is tightened to just credential capture and storage (removing Keycloak user creation, email, and revocation), it could fit. Borderline — tighten scope or decompose.

    Recommendations

    • [BODY] Fix file target: app/models/user.rb should say "(new — create User model with encrypted credential fields)" since the file and users table do not exist
    • [BODY] Fix migration description: "create users table with encrypted credential columns" not "add credential columns"
    • [BODY] Add missing file target: mailer for welcome email (Scope item 5)
    • [BODY] Add missing file target: Keycloak admin API service for programmatic user creation (Scope item 4)
    • [BODY] Specify encryption approach and add gem to Gemfile if needed (lockbox, attr_encrypted, or Rails encrypted attributes)
    • [BODY] Either remove AC #6 (credential revocation) to a separate ticket, or add file targets for credential management controller/view
    • [BODY] Clarify whether this ticket wires per-user credentials into KalshiClient/OrderService or if that is a separate integration ticket (blast radius: 4+ service/job files not listed)
    • [SCOPE] Create architecture note arch-rails for component rails
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Sprint 4, depends on #26 and bots running
    • [x] Repo -- ldraney/prediction-assistant
    • [x] User Story -- Bot Marketplace
    • [x] Context -- detailed background
    • [x] File Targets -- 7 files listed
    • [x] Feature Flag -- None (integral to UI)
    • [x] Acceptance Criteria -- 10 criteria
    • [x] Test Expectations -- listed
    • [x] Constraints -- listed
    • [x] Checklist -- 11 items
    • [x] Related -- dependencies listed

    All required sections present per template-issue-feature.

    Traceability

    • [x] story:bot-marketplace label -- Bot Marketplace user story
    • [x] story note verified -- found in project-prediction-assistant user-stories section
    • [x] arch:frontend label -- frontend component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-frontend for component frontend
    • [x] Forgejo issue -- #30, open

    File Targets

    • [x] app/models/activity_log.rb -- EXISTS but schema diverges from AC (see Recommendations)
    • [x] db/migrate/20260703200001_create_activity_logs.rb -- EXISTS, matches actual model
    • [ ] app/channels/bot_activity_channel.rb -- does not exist yet, to be created
    • [ ] app/views/bots/_activity_feed.html.erb -- does not exist yet, to be created
    • [ ] app/views/activity_logs/_activity_log.html.erb -- does not exist yet, to be created
    • [x] test/models/activity_log_test.rb -- EXISTS with full validation/scope coverage
    • [ ] test/channels/bot_activity_channel_test.rb -- does not exist yet, to be created

    Critical mismatch: AC #1 specifies fields that do not match the existing ActivityLog model:

    • AC says user_id -- no User model exists in the app, no user_id on ActivityLog or Strategy
    • AC says bot_type -- actual field is bot_name
    • AC says action values (order_placed, fill_received, position_opened, exit_triggered, error) -- actual values are place_order, cancel_order, fill, partial_fill, error, dry_run, bulk_sweep
    • AC says rationale (text) -- actual fields are message (text) + metadata (jsonb)
    • AC says timestamp -- actual field is created_at (standard Rails timestamps)

    Repo Placement

    OK -- issue filed on ldraney/prediction-assistant, all file targets in that repo. Single-repo scope.

    Dependencies

    • #26 (Bot Detail pages) -- in validation column, merged. Show page exists at app/views/bots/show.html.erb. SATISFIED.
    • #23 (OrderService) -- in validation column. Already creates ActivityLog entries via ActivityLog.create!. SATISFIED.
    • #27 (Pregame Stacker) -- in validation column, merged. SATISFIED.
    • #28 (Bulk Sweep) -- in validation column, merged. SATISFIED.
    • #29 (Edge Learner) -- in backlog (sprint:6). NOT blocking -- feed works with whatever bots exist.
    • #48 (Hotwire conversion) -- in validation column. Turbo/Stimulus/Action Cable are available. SATISFIED.
    • Undocumented: User authentication model -- AC #6 says scope by user_id but no User model exists. Keycloak OIDC is integrated (#57, closed) but no Rails User model has been created. Action Cable Connection has no auth.

    Acceptance Criteria

    • AC #1 (model fields) -- STALE, model already exists with different schema. Needs update.
    • AC #2 (Kalshi market link) -- testable, straightforward
    • AC #3 (feed partial on detail page) -- testable, show.html.erb exists to embed it
    • AC #4 (per-bot filtering) -- testable, for_bot scope exists
    • AC #5 (Turbo Streams real-time) -- testable, Action Cable + solid_cable configured
    • AC #6 (channel scoped to user_id + strategy_id) -- PROBLEMATIC: no User model, no user_id. Must scope by strategy_id only for now.
    • AC #7 (pagination) -- testable
    • AC #8 (entry display: rationale) -- STALE: field is message, not rationale
    • AC #9 (entries created by bots/OrderService) -- ALREADY MET: OrderService already creates ActivityLog entries
    • AC #10 (reverse chronological) -- testable, recent scope exists

    Blast Radius

    • OrderService already creates ActivityLog entries -- adding after_create broadcast will fire on every trade execution across all bots
    • BulkSweepJob processes many markets rapidly -- broadcasting per-entry could flood the cable. Constraints section mentions throttling but no specific mechanism is prescribed
    • Action Cable uses solid_cable (database-backed pub/sub, not Redis) -- performance under high-frequency broadcasting should be monitored
    • No cross-user leakage concern currently since there is no multi-user model yet

    Decomposition Assessment

    7 file targets (3 already exist), 10 acceptance criteria (exceeds 5 threshold), 1 repo. Remaining work is 4 new files + 3 modifications. The work is standard Rails patterns (channel, partials, Turbo Streams wiring) that are boilerplate-heavy but not complex. At 2 story points the scope is reasonable for a single agent pass once the AC is corrected, provided AC #9 is marked as pre-existing. No decomposition needed after AC cleanup -- effective AC count drops to 7-8, and the remaining file creation is templated.

    Recommendation

    • [BODY] Update AC #1 field list to match existing ActivityLog schema: strategy_id, bot_name (not bot_type), action (place_order, cancel_order, fill, partial_fill, error, dry_run, bulk_sweep), ticker, side, quantity, price, status (success, failure, dry_run, rejected), message (not rationale), metadata (jsonb). Remove user_id and timestamp.
    • [BODY] Update AC #6: remove user_id scoping -- scope BotActivityChannel to strategy_id only. User scoping is a future concern when User model lands.
    • [BODY] Update AC #8: replace "rationale" with "message" to match actual schema.
    • [BODY] Mark AC #9 as pre-existing (OrderService already writes to ActivityLog) or reframe as "verify existing integration".
    • [SCOPE] Create architecture note arch-frontend for component frontend.
  • 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 12 required sections present per template-issue-feature.

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- story-prediction-assistant-portfolio-builder exists in pal-e-docs (active, user-story type)
    • [x] arch:rails label -- Rails component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-rails for component rails. Search for "arch-rails" returned no results.
    • [x] Forgejo issue -- ldraney/prediction-assistant#29, state: open

    File Targets

    • [x] app/jobs/edge_learner_job.rb -- new file, pattern consistent with existing jobs (bulk_sweep_job.rb, pregame_threshold_job.rb)
    • [x] app/models/price_pattern.rb -- new file, pattern consistent with existing models
    • [x] app/services/group_builder.rb -- new file, pattern consistent with existing services
    • [x] test/jobs/edge_learner_job_test.rb -- new file
    • [x] test/models/price_pattern_test.rb -- new file
    • [x] test/services/group_builder_test.rb -- new file
    • [ ] MISSING: db/migrate/YYYYMMDDHHMMSS_create_price_patterns.rb -- PricePattern model requires a database table but no migration is listed. Schema needs columns for sport, game_situation, entry_price, exit_price, hold_duration, confidence, etc.
    • [ ] MISSING: app/jobs/price_update_job.rb -- currently a stub ("bot consumers implement in Sprint 4"). Needs modification to fan out to EdgeLearnerJob.
    • [ ] MISSING: app/jobs/time_update_job.rb -- currently a stub ("bot consumers implement in Sprint 4"). Needs modification to fan out to EdgeLearnerJob.
    • [ ] MISSING: app/jobs/sweep_eligible_job.rb -- has comment "Edge Learner will be added in Sprint 4 (#29)". Needs modification to fan out to EdgeLearnerJob alongside BulkSweepJob.

    Repo Placement

    OK -- issue filed on ldraney/prediction-assistant, ### Repo field says ldraney/prediction-assistant. Single repo, no cross-repo concerns.

    Dependencies

    • #2 (MarketScanner emitting events) -- board item 1687, validation column. Code exists, not yet validated.
    • #23 (OrderService) -- board item 1710, validation column. Code exists at app/services/order_service.rb.
    • #24 (PositionTracker) -- board item 1711, validation column. Code exists at app/services/position_tracker.rb.
    • #3 (EdgeLearnerConfig STI) -- board item 1688, validation column. Code exists at app/models/edge_learner_config.rb.

    All 4 dependencies are correctly documented. All are in validation column (code exists, awaiting final validation). Not blocking implementation.

    Acceptance Criteria

    15 acceptance criteria. All are individually testable by an agent (group counts are deterministic, risk levels have clear behavior, OrderService usage is greppable). However, 15 AC is 3x the decomposition threshold (5). The criteria span 4 independent sub-systems (Group Builder, Portfolio Monitor, Predictive Entry, Risk-Adjusted Closer) plus infrastructure (fan-out wiring, PricePattern model/migration).

    Blast Radius

    • EdgeLearnerConfig.max_group_size validation bug: The existing model (from #3) validates max_group_size as a positive_integer, but both the issue spec and docs/bots/edge-learner.md define it as an enum ("pairs", "triples", "uncapped"). This mismatch will cause validation failures when the job tries to use string values. Needs fix in edge_learner_config.rb line 25.
    • Fan-out jobs: 3 existing fan-out jobs (price_update_job.rb, time_update_job.rb, sweep_eligible_job.rb) need modification to dispatch to EdgeLearnerJob. These are not listed as file targets.
    • No downstream consumers affected: Edge Learner is a leaf consumer -- it reads events and places orders, no other bots depend on it.
    • Lineage inconsistency: Issue body says "Sprint 5" but board item label says sprint:6.

    Decomposition Assessment

    NEEDS DECOMPOSITION -- route to skill-decompose-ticket.

    • 6 new file targets + 3 missing modification targets + 1 missing migration = 10 files total
    • 15 acceptance criteria (3x the 5-AC threshold)
    • 4 independent sub-systems (Group Builder, Portfolio Monitor, Predictive Entry, Risk-Adjusted Closer)
    • Estimated agent work: 20-30 minutes for a single pass (well over 5-minute rule)

    Suggested decomposition: split into sub-tickets per sub-system (Group Builder, Portfolio Monitor, Predictive Entry + PricePattern model, Risk-Adjusted Closer) plus a wiring ticket for fan-out job updates and EdgeLearnerJob shell.

    Recommendation

    • [BODY] Add migration file target: db/migrate/YYYYMMDDHHMMSS_create_price_patterns.rb -- PricePattern model needs a database table
    • [BODY] Add fan-out job modification targets: app/jobs/price_update_job.rb, app/jobs/time_update_job.rb, app/jobs/sweep_eligible_job.rb
    • [BODY] Fix Lineage: says "Sprint 5" but board item is labeled sprint:6
    • [BODY] Note pre-existing bug: EdgeLearnerConfig validates max_group_size as positive_integer but spec requires enum ("pairs", "triples", "uncapped") -- fix needed in edge_learner_config.rb
    • [SCOPE] Create architecture note arch-rails for component rails
    • [DECOMPOSE] 15 AC across 4 sub-systems + infrastructure, estimated 20-30 min agent work. Route to skill-decompose-ticket.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — present ("Depends on all backend tickets (#1-#5)")
    • [x] Repo — present (ldraney/kalshi-assistant)
    • [x] User Story — full As a / I want / So that format
    • [x] Context — good description with user flow reference
    • [ ] File Targets — present but "TBD" — not actionable for an agent
    • [x] Feature Flag — "none"
    • [x] Acceptance Criteria — 6 criteria present
    • [ ] Test Expectations — present but "TBD based on framework choice" — not actionable
    • [x] Constraints — 3 constraints present
    • [x] Checklist — present
    • [x] Related — present

    Traceability

    • [x] story:app-experience — App Experience — verified in project-kalshi-assistant user-stories section
    • [x] story:credential-onboarding — Credential Onboarding — verified in project-kalshi-assistant user-stories section
    • [ ] arch:frontend — arch note MISSING — [SCOPE] Create architecture note arch-frontend for component frontend
    • [ ] arch:auth — arch note MISSING — [SCOPE] Create architecture note arch-auth for component auth
    • [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/ldraney/kalshi-assistant/issues/6, open

    File Targets

    • [ ] File Targets are "TBD — depends on frontend framework decision (React Native / Flutter / Rails Turbo Native)" — no concrete paths to verify. The codebase currently contains only docs (no application code). An agent cannot act on this ticket until file targets are specified.

    Repo Placement

    Issue is filed on ldraney/kalshi-assistant and the Repo section matches. However, a mobile app (React Native or Flutter) may warrant a separate repo. This decision is blocked on the framework choice. If the frontend lives in a separate repo, the Forgejo issue should be refiled there.

    Dependencies

    • Documented: #1-#5 (all backend tickets). All are in backlog. Sprint:4 sequencing is correct — this ticket runs after sprint:1-3 backend work.
    • Undocumented: #10 (Keycloak realm, client, users, and login theme — board item #1693, sprint:1, arch:keycloak,arch:auth) is a clear dependency for the auth flow. The mobile app needs Keycloak for user login and session management, but #10 is not listed in the Lineage section.
    • All dependencies are in backlog — none are blocking this ticket from scoping, but all must be completed before implementation.

    Acceptance Criteria

    6 criteria spanning multiple feature areas:

    • Auth: API key + private key input screen with validation; User can revoke API access
    • Strategy: Strategy selection (Watchdog, Option D); Budget configuration per strategy
    • Dashboard: Live dashboard: active positions, unrealized P&L
    • History: Trade history log

    Each criterion is testable in principle, but "Live dashboard: active positions, unrealized P&L" is vague — it should specify what data source, refresh interval, and failure states. The criteria span at least 4 distinct screens/features, making this too broad for a single agent pass.

    Blast Radius

    Limited. No existing frontend code to break. The codebase contains only documentation. This ticket introduces a new surface area. Downstream effects: the auth flow touches Keycloak (ticket #10) and all API endpoints from backend tickets #1-#5.

    Decomposition Assessment

    NEEDS DECOMPOSITION — route to skill-decompose-ticket

    • 8 points (largest ticket on the board)
    • 6 acceptance criteria spanning 4+ feature areas (auth, strategy config, live dashboard, trade history)
    • File targets TBD — likely many files for a full mobile app
    • Framework decision is an unresolved prerequisite — should be a separate Spike ticket
    • Estimated agent work: well over 5 minutes (full mobile app with auth, multiple screens, real-time data)

    Recommendation

    • [SCOPE] Create architecture note arch-frontend for component frontend
    • [SCOPE] Create architecture note arch-auth for component auth
    • [SCOPE] Framework decision (React Native / Flutter / Rails Turbo Native) should be resolved first — consider creating a Spike ticket to evaluate options and make this decision before decomposing
    • [BODY] File Targets are "TBD" — need concrete paths after framework decision is made
    • [BODY] Test Expectations are "TBD" — need concrete test commands after framework decision
    • [BODY] Add Keycloak ticket (#10) to Lineage as a dependency: "Depends on #10 (Keycloak) and all backend tickets (#1-#5)"
    • [DECOMPOSE] 8 points, 6 AC across 4 feature areas (auth, strategy, dashboard, history) — route to skill-decompose-ticket. Suggested split: (1) Spike: framework decision, (2) API credential input + validation, (3) Strategy selection + budget config, (4) Live dashboard, (5) Trade history, (6) Access revocation
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

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

    All required sections present per template-issue-feature.

    Traceability

    • [x] story:platform-setup label -- Platform Setup
    • [x] story note verified -- found in project-kalshi-assistant user-stories section, links to story-kalshi-assistant-platform-setup
    • [x] arch:kustomize label -- Kustomize overlays
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-kustomize
    • [x] arch:k8s-deploy label -- Kubernetes deployment
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-k8s-deploy
    • [x] Forgejo issue -- ldraney/kalshi-assistant#9, open

    File Targets

    • [x] overlays/kalshi-assistant/prod/kustomization.yaml -- to create; matches landscaping-assistant pattern
    • [x] overlays/kalshi-assistant/prod/deployment-patch.yaml -- to create; matches landscaping-assistant pattern
    • [x] overlays/kalshi-assistant/prod/secrets.enc.yaml -- to create; matches landscaping-assistant pattern
    • [x] overlays/kalshi-assistant/dev/kustomization.yaml -- to create
    • [ ] overlays/kalshi-assistant/dev/deployment.yaml -- to create; ISSUE: dev overlay is incomplete

    Dev overlay gap: The issue says "Following the landscaping-assistant model" but the dev file targets list only 2 files (kustomization.yaml, deployment.yaml). The landscaping-assistant dev overlay has 6 files: configmap.yaml, deployment.yaml, ingress.yaml, kustomization.yaml, namespace.yaml, service.yaml. The missing files (configmap, ingress, namespace, service) are required for a functional dev overlay. Without them, kustomize build for dev will fail.

    Prod kustomization.yaml gap: The landscaping-assistant prod kustomization.yaml contains extensive inline patches for renaming Deployment, Service, and ServiceMonitor resources from the generic "app" name to the project name. These rename patches are not mentioned in the issue body or file targets. The implementing agent would need to infer them from the reference pattern.

    Repo Placement

    Mismatch: The issue ### Repo field correctly identifies ldraney/pal-e-deployments as the target repo, but the Forgejo issue is filed on ldraney/kalshi-assistant. The implementing agent's PR must target pal-e-deployments, not kalshi-assistant. This cross-repo tracking is acceptable if intentional (tracking deployment work under the project), but the agent must be explicitly directed to open the PR on the correct repo.

    Dependencies

    Undocumented dependencies found on the board:

    • #1694 "Service onboarding -- namespace, Harbor, ArgoCD, Tailscale funnel" (sprint:1, backlog) -- prerequisite. Kustomize overlays need the namespace and ArgoCD application to exist. This is sprint:1 work that must complete before sprint:2 overlays.
    • #1686 "Rails app scaffold with Kalshi API client" (sprint:1, backlog) -- prerequisite. The container image must exist in Harbor before the deployment can reference it.
    • #1696 "CI/CD pipeline -- Woodpecker config" (sprint:2, backlog) -- sibling. CI pipeline pushes images that the overlay references, but overlays can be created first with placeholder tags.

    None of these dependencies are documented in the issue scope.

    Acceptance Criteria

    6 acceptance criteria, all verifiable via kustomize build output inspection:

    • [x] AC1: kustomize build renders valid manifests -- directly testable
    • [x] AC2: SOPS encryption -- testable (encryption verifiable; cluster decryption requires live cluster)
    • [x] AC3: Init container runs db:prepare -- verifiable in rendered YAML
    • [x] AC4: Probes on /up:3000 -- verifiable in rendered YAML
    • [x] AC5: Resource limits -- verifiable in rendered YAML
    • [x] AC6: Security context -- verifiable in rendered YAML

    Missing AC: No acceptance criterion for the dev overlay. Test Expectations only mention kustomize build for both overlays but AC only describes prod characteristics. Dev overlay should have its own AC (nginx proxy to dev machine IP, correct namespace kalshi-dev).

    Blast Radius

    Low risk. 15+ services already use this overlay pattern in pal-e-deployments. The work is additive (new directory) and does not modify any existing overlays or bases. No downstream consumers are affected.

    Decomposition Assessment

    5 file targets in 1 repo (below the >3 files across >2 repos threshold). 6 AC (marginally above >5 threshold). However, all AC describe configuration aspects of a single deployment-patch.yaml file. The work is highly formulaic -- copying the landscaping-assistant pattern and adjusting names, secrets, and ports. Estimated agent time: 3-4 minutes. No decomposition needed.

    Recommendation

    • [BODY] Add missing dev overlay file targets: configmap.yaml, ingress.yaml, namespace.yaml, service.yaml (per landscaping-assistant reference pattern)
    • [BODY] Document prod kustomization.yaml inline rename patches (Deployment, Service, ServiceMonitor) in File Targets or Context
    • [BODY] Add dependency documentation: blocked by #1694 (service onboarding) and #1686 (Rails app scaffold)
    • [BODY] Add dev-specific acceptance criteria (nginx proxy, kalshi-dev namespace)
    • [SCOPE] Create architecture note arch-kustomize for component kustomize
    • [SCOPE] Create architecture note arch-k8s-deploy for component k8s-deploy
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone
    • [x] Repo -- ldraney/pal-e-services
    • [x] User Story -- present, well-formed
    • [x] Context -- present, describes foundational nature
    • [x] File Targets -- present, with both modify and do-not-touch sections
    • [x] Feature Flag -- none (appropriate for infra ticket)
    • [x] Acceptance Criteria -- 5 items
    • [x] Test Expectations -- present with run command
    • [x] Constraints -- present, references landscaping-assistant pattern
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:platform-setup label -- Platform Setup
    • [x] story note verified -- story-kalshi-assistant-platform-setup exists in pal-e-docs, listed in project-kalshi-assistant user-stories section
    • [x] arch:iac label -- Infrastructure as Code
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-iac for component iac
    • [x] arch:k8s-deploy label -- Kubernetes Deployment
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-k8s-deploy for component k8s-deploy
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/kalshi-assistant/issues/7, open

    File Targets

    • [x] terraform/k3s.tfvars -- verified: file exists (symlink to ~/secrets/pal-e-services/k3s.tfvars), services map starts at line 193, landscaping-assistant pattern confirmed at lines 203-210 with matching structure (forgejo_repo, image_repo, port 3000, funnel true, source_repo, source_path). No existing kalshi-assistant entry -- clean for addition.

    Repo Placement

    Issue filed on ldraney/kalshi-assistant but ### Repo section explicitly declares work is in ldraney/pal-e-services. This is an acceptable pattern: project-specific infrastructure tickets are filed on the project repo, with the shared infrastructure repo declared as the target. Work touches only one repo (pal-e-services). No multi-repo issue needed.

    Dependencies

    This ticket is correctly identified as foundational in Context: "nothing else deploys until this is done." Board analysis confirms downstream dependencies:

    • #1686 Rails app scaffold (sprint:1) -- blocked by this ticket (needs namespace)
    • #1692 DNS + reverse proxy (sprint:1) -- depends on namespace/funnel existing
    • #1693 Keycloak realm (sprint:1) -- depends on namespace existing
    • #1695 Kustomize overlays (sprint:2) -- depends on ArgoCD app existing
    • #1696 CI/CD pipeline (sprint:2) -- depends on Harbor project existing

    Dependencies are documented implicitly in Context but not explicitly enumerated. Acceptable for a clearly foundational ticket.

    Acceptance Criteria

    All 5 criteria are verifiable by an agent:

    • tofu plan -- exits 0 with expected resources (testable)
    • tofu apply -- succeeds (testable, creates real resources)
    • Namespace check -- kubectl get ns kalshi-assistant (testable)
    • Harbor project check -- Harbor API or UI (testable)
    • ArgoCD app check -- argocd app get kalshi-assistant (testable)

    Test Expectations section includes a run command: cd ~/pal-e-services && tofu plan -var-file=terraform/k3s.tfvars. Good.

    Blast Radius

    Low. Adds a new entry to a shared tfvars file without modifying existing entries. The pattern is well-established with multiple existing service entries (landscaping-assistant, believers-elite, mdview, gcal-scheduler, etc.). No downstream consumers affected by addition.

    Decomposition Assessment

    No decomposition needed:

    • 1 file target in 1 repo
    • 5 acceptance criteria (at threshold, not over)
    • Estimated agent work: under 2 minutes (single block addition following a clear pattern)

    Recommendation

    • [SCOPE] Create architecture note arch-iac documenting the IaC patterns (Terraform/OpenTofu modules, tfvars structure, service map convention)
    • [SCOPE] Create architecture note arch-k8s-deploy documenting the k8s deployment pipeline (ArgoCD, Image Updater, namespace provisioning, Tailscale funnel)

    These are platform-level architecture notes that would serve all projects using the pal-e-platform infrastructure, not just kalshi-assistant. The ticket scope itself is solid -- clear target, verified pattern, testable criteria. The missing arch notes are a documentation gap, not a scope problem.

  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- present (depends on #3)
    • [x] Repo -- present
    • [x] User Story -- present
    • [x] Context -- present, references docs/strategy/sizing-models.md
    • [x] File Targets -- present (2 files to create)
    • [x] Feature Flag -- "none"
    • [x] Acceptance Criteria -- present (6 items)
    • [x] Test Expectations -- present (3 tests + run command)
    • [x] Constraints -- present
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- story-kalshi-assistant-portfolio-builder exists in pal-e-docs, listed in project-kalshi-assistant user-stories section
    • [x] arch:app label -- application component
    • [ ] arch note MISSING -- [SCOPE] Create architecture note arch-app for the Rails application component
    • [x] Forgejo issue -- ldraney/kalshi-assistant#5, open

    File Targets

    • [ ] app/services/sizing_engine.rb -- ISSUE: file to be created, but the app/ directory does not exist. The repo is currently docs-only. Rails scaffold (issue #1) must be completed first to establish the app/services/ directory structure.
    • [ ] app/services/scenario_analyzer.rb -- ISSUE: same as above -- depends on Rails scaffold existing.
    • [x] docs/strategy/sizing-models.md -- verified: contains Option D math (83/17 split, -$5.77 worst case, all scenario tables). Context reference is accurate.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant, work targets ldraney/kalshi-assistant. Single repo, no cross-repo concerns.

    Dependencies

    • Declared: #3 (Strategy engine -- database schema, board item #1688, sprint:2, backlog). Correct -- the sizing engine needs the strategy/trade schema.
    • Undocumented: #1 (Rails app scaffold, board item #1686, sprint:1, backlog). The app/services/ path assumes a Rails app structure that does not yet exist. This is a hard blocker -- without the Rails framework, there is no directory to create services in and no test runner to execute rails test.
    • Both dependencies are in the backlog column. This ticket (sprint:3) cannot start until sprint:1 (#1) and sprint:2 (#3) complete.

    Acceptance Criteria

    6 acceptance criteria, all concrete and agent-verifiable:

    • Input/output contracts are well-defined (picks array, budget, contract allocations)
    • Optimization target is clear (brute-force over 50-99% YES/NO ratio)
    • Scenario table output is specified (2^N outcomes with probability and P&L)
    • Scale constraint is bounded (1-10 picks, max 1024 scenarios)
    • Test expectations reference concrete values from docs (83/17 ratio, -$5.77 worst case) which are verified in docs/strategy/sizing-models.md
    • Run command (rails test) is standard for Rails

    Criteria are testable. However, count of 6 AC exceeds the >5 decomposition threshold.

    Blast Radius

    Low. This creates new service classes in a greenfield codebase. No existing code patterns to search for similar issues. No downstream consumers at this stage. The sizing engine is self-contained computation with no external integrations.

    Decomposition Assessment

    • File targets: 2 files in 1 repo -- OK (below >3 across >2 repos threshold)
    • Acceptance criteria: 6 -- exceeds >5 threshold
    • Estimated agent time: under 5 minutes -- the math is fully documented in sizing-models.md, and the two services are tightly coupled (scenario_analyzer feeds sizing_engine)

    One of three decomposition triggers fires (6 AC > 5). However, the work is tightly coupled -- SizingEngine and ScenarioAnalyzer form a single computation pipeline. Splitting them into separate tickets would create artificial boundaries and add coordination overhead. The 6 AC describes aspects of a single algorithm, not independent features. Recommend the author either consolidate to 5 AC (e.g., merge "returns optimal ratio" into the output AC) or accept the borderline count given the tight coupling.

    Recommendation

    • [BODY] Add dependency on #1 (Rails app scaffold) to the Lineage section. Current text mentions only #3 (schema), but the app/services/ path requires the Rails framework to exist. Suggested: "Depends on ldraney/kalshi-assistant #1 (scaffold) and #3 (schema)."
    • [SCOPE] Create architecture note arch-app for the Rails application component. The arch:app label on this and other board items has no backing architecture note in pal-e-docs.
    • [BODY] Consider consolidating AC from 6 to 5 by merging "Returns optimal ratio and expected value" into "Output: contracts to buy for each YES and NO position" (both describe output shape). This avoids the >5 decomposition trigger. Alternatively, accept the 6 AC given the tight coupling of the work.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    Issue type: Feature (template-issue-feature)

    • [x] Type -- "Feature"
    • [x] Lineage -- documents deps on #2 (scanner) and #3 (schema)
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- present, well-formed
    • [x] Context -- references watchdog-strategy.md, API endpoint verified
    • [x] File Targets -- 3 CREATE targets listed
    • [x] Feature Flag -- watchdog_live_trading, global, disabled by default
    • [x] Acceptance Criteria -- 6 criteria listed
    • [x] Test Expectations -- unit tests + run command
    • [x] Constraints -- dry-run default, logging, error handling
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:watchdog-trading label -- verified in project-kalshi-assistant user-stories section
    • [x] story note verified -- "Watchdog Trading" entry exists with success metric "85%+ win rate, $0.10-$0.15 avg profit per contract"
    • [ ] arch:app note MISSING -- [SCOPE] No arch-app note exists in pal-e-docs. Project page references arch-domain-kalshi-assistant, arch-dataflow-kalshi-assistant, arch-deployment-kalshi-assistant instead. Label may need realignment.
    • [ ] arch:api note MISSING -- [SCOPE] No arch-api note exists in pal-e-docs. Same mismatch with project page architecture naming.
    • [x] Forgejo issue -- ldraney/kalshi-assistant#4, state: open

    File Targets

    • [ ] app/services/watchdog_service.rb -- ISSUE: app/ directory does not exist yet. Requires Rails scaffold (issue #1) to be completed first. Correctly marked as CREATE target.
    • [ ] app/jobs/watchdog_job.rb -- ISSUE: Same dependency on #1 for app/ directory to exist.
    • [ ] app/services/order_service.rb -- ISSUE: Same dependency. Also note: this is a shared service that Option D sizing (#5) will likely also need.

    The referenced docs/strategy/watchdog-strategy.md DOES exist and confirms the strategy description. API endpoint POST /portfolio/events/orders with side: bid confirmed in openapi.yaml (line 1143). The strategy doc uses the full path /trade-api/v2/portfolio/events/orders.

    Repo Placement

    OK -- issue filed on ldraney/kalshi-assistant, all file targets are in the same repo. Single-repo ticket.

    Dependencies

    • Documented: #2 Market scanner (sprint:2, backlog), #3 Strategy engine schema (sprint:2, backlog)
    • Undocumented: #1 Rails app scaffold (sprint:1, backlog) -- the app/ directory structure comes from the scaffold. This ticket cannot be implemented without #1.
    • Sprint ordering: Correct -- this is sprint:3, dependencies are sprint:1 and sprint:2
    • Board state: All 12 items are in backlog. No items are in_progress or blocking.
    • Downstream: #5 Option D sizing (sprint:3) may share order_service.rb

    Acceptance Criteria

    6 criteria, all verifiable by an agent:

    • [x] Threshold detection (yes_bid >= confidence_threshold) -- unit testable
    • [x] API order placement (POST /portfolio/events/orders, side: bid) -- integration testable with HTTP mocks
    • [x] Trade record creation with order_id -- DB assertion testable
    • [x] Sell triggers at 95%+ (take profit) and below 80% (cut loss) -- unit testable
    • [x] Dry-run mode logs without executing -- unit testable with feature flag mock
    • [x] Pick and Trade record creation -- DB assertion testable

    All criteria are concrete and testable. Count of 6 exceeds the >5 decomposition threshold.

    Blast Radius

    Greenfield project -- no existing application code to conflict with. order_service.rb is a candidate shared service (also needed by #5 Option D sizing). No sibling services or downstream consumers exist yet.

    Decomposition Assessment

    • 3 file targets in 1 repo -- OK (under the >3 files across >2 repos threshold)
    • 6 acceptance criteria -- exceeds the >5 threshold by 1
    • Estimated agent work -- ~5 minutes, borderline

    Borderline decomposition case. The natural split would extract order_service.rb (shared API client for order placement) into a separate foundational ticket, reducing this ticket to 4 AC focused on watchdog-specific logic. This also benefits #5 (Option D sizing) which will need the same order service.

    Recommendations

    • [BODY] Add issue #1 (Rails app scaffold) to Lineage dependencies. The app/ directory does not exist without it.
    • [LABEL] Align arch labels with project page architecture notes. The project page defines arch-domain-kalshi-assistant, arch-dataflow-kalshi-assistant, arch-deployment-kalshi-assistant -- but the board item uses arch:app and arch:api which have no backing notes.
    • [SCOPE] Create architecture notes for this project. Either create arch-app and arch-api notes to match the current labels, or realign labels to the project page's architecture naming (arch-domain, arch-dataflow, arch-deployment) and create those notes.
    • [DECOMPOSE] 6 AC exceeds >5 threshold. Recommend extracting order_service.rb into a separate foundational ticket (shared Kalshi API order client), reducing this ticket to 4 AC. The order service is also needed by #5 (Option D sizing).
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Depends on ldraney/kalshi-assistant #1
    • [x] Repo — ldraney/kalshi-assistant
    • [x] User Story — As the watchdog system / scan same-day markets / detect 85% threshold
    • [x] Context — Watchdog strategy background, series tickers, rate limits
    • [x] File Targets — 4 files to create
    • [x] Feature Flag — none
    • [x] Acceptance Criteria — 6 items
    • [x] Test Expectations — unit + integration tests, run command
    • [x] Constraints — Solid Queue, rate limits, 24h history
    • [x] Checklist — PR, tests, no unrelated changes
    • [x] Related — project reference

    All sections present per template-issue-feature.

    Traceability

    • [x] story:watchdog-trading label — Watchdog Trading
    • [x] story note verified — story-kalshi-assistant-watchdog-trading exists (active, user-story type)
    • [x] story entry verified — found in project-kalshi-assistant user-stories table
    • [x] arch:api label present
    • [ ] arch note MISSING — [SCOPE] Create architecture note arch-api for component api
    • [x] arch:app label present
    • [ ] arch note MISSING — [SCOPE] Create architecture note arch-app for component app
    • [ ] arch label mismatch — [LABEL] Story note references arch-domain-kalshi-assistant and arch-dataflow-kalshi-assistant, but board item has arch:api and arch:app. These should be reconciled.
    • [x] Forgejo issue — ldraney/kalshi-assistant#2, open

    File Targets

    • [x] app/jobs/market_scanner_job.rb — to create (Solid Queue job). No app/ directory exists yet; expected since this depends on issue #1 (Rails scaffold).
    • [x] app/models/market_snapshot.rb — to create (snapshot model). Same dependency on #1.
    • [x] db/migrate/xxx_create_market_snapshots.rb — to create (migration). Same dependency on #1.
    • [x] config/market_series.yml — to create (series config). Same dependency on #1.

    All file targets are files to CREATE, not modify. File paths follow Rails conventions. The repo has no Rails structure yet because the prerequisite (issue #1 — Rails scaffold) hasn't been completed. File targets are valid assuming #1 is done first.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant, Repo section says ldraney/kalshi-assistant. Single repo, no multi-repo concern.

    Dependencies

    • Upstream: Depends on issue #1 (Rails scaffold + API client) — documented in Lineage. Board item #1686 is in backlog (sprint:1). This ticket is sprint:2. Correct ordering.
    • Downstream: Board item #1689 "Watchdog — auto-buy when market crosses 85% threshold" (sprint:3, issue #4) logically depends on this scanner. Not documented in this ticket's scope but is in the correct sprint order.
    • Parallel: Board item #1688 "Strategy engine — database schema" (sprint:2, issue #3) is in the same sprint. No apparent conflict — these can run in parallel once #1 is complete.

    Acceptance Criteria

    6 criteria, all testable by an agent:

    • [x] Background job polls — verifiable via test that job enqueues and runs
    • [x] Scans configured series — verifiable via config iteration test
    • [x] Stores snapshots with specified fields — verifiable via DB assertions
    • [x] Filters same-day/next-day — verifiable via date filtering test
    • [x] Respects rate limits — verifiable via configurable delay check
    • [x] Logs 85% threshold crossing — verifiable via log output assertion

    Test expectations are reasonable: unit tests for model validations and filtering logic, integration test for full job execution. Run command rails test is standard Rails.

    Blast Radius

    • New repo with no existing production code (only docs and README). Minimal blast radius.
    • Referenced docs exist: docs/api/market-data-guide.md and docs/strategy/watchdog-strategy.md both present in repo.
    • Ticker discrepancy: Issue Context lists 11 series tickers including crypto tickers (KXBTC15M, KXETH15M, KXSOL15M, KXDOGE15M, KXXRP15M, KXBNB15M, KXNEAR15M, KXHYPE15M) and KXWNBA. The market-data-guide.md only documents KXMLBGAME, KXWCGAME, KXMLB, KXNBA, KXNHL. The additional tickers in the issue are not sourced. This should be reconciled — either update the market data guide or cite the source for these tickers in the issue body.

    Decomposition Assessment

    • 4 file targets across 1 repo — under the >3 files across >2 repos threshold.
    • 6 acceptance criteria — borderline exceeds the >5 threshold by 1.
    • Estimated agent work: ~5 minutes. Creating 4 tightly coupled files (model, job, migration, config) plus tests for a single feature.

    The 6 AC are all aspects of a single scanner component. Decomposition would create artificial sub-tickets with heavy coupling. No decomposition needed — the borderline AC count does not warrant splitting this coherent feature.

    Recommendation

    • [SCOPE] Create architecture note arch-api for the Kalshi API client component.
    • [SCOPE] Create architecture note arch-app for the application layer component.
    • [LABEL] Reconcile arch labels: story note references arch-domain-kalshi-assistant and arch-dataflow-kalshi-assistant, but board item uses arch:api and arch:app. Decide which taxonomy to use and align both.
    • [BODY] Cite source for crypto and WNBA tickers in Context section (KXBTC15M, KXETH15M, KXSOL15M, KXDOGE15M, KXXRP15M, KXBNB15M, KXNEAR15M, KXHYPE15M, KXWNBA) or update docs/api/market-data-guide.md to include them.
  • Verdict: APPROVED

    Re-review after refinement. Previous review (review-1694-2026-07-02) found two missing architecture notes. Both have been created and verified.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone
    • [x] Repo -- ldraney/pal-e-services
    • [x] User Story -- present, well-formed
    • [x] Context -- present, describes foundational nature
    • [x] File Targets -- present, with both modify and do-not-touch sections
    • [x] Feature Flag -- none (appropriate for infra ticket)
    • [x] Acceptance Criteria -- 5 items
    • [x] Test Expectations -- present with run command
    • [x] Constraints -- present, references landscaping-assistant pattern
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:platform-setup label -- Platform Setup
    • [x] story note verified -- story-kalshi-assistant-platform-setup listed in project-kalshi-assistant user-stories section
    • [x] arch:iac label -- Infrastructure as Code
    • [x] arch note verified -- arch-iac note exists (type: architecture, status: active, project: kalshi-assistant). Sections: Diagram, Components, Key Decisions, Related.
    • [x] arch:k8s-deploy label -- Kubernetes Deployment
    • [x] arch note verified -- arch-k8s-deploy note exists (type: architecture, status: active, project: kalshi-assistant). Sections: Diagram, Components, Key Decisions, Related.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/kalshi-assistant/issues/7, open

    File Targets

    • [x] terraform/k3s.tfvars -- verified: file exists (symlink to ~/secrets/pal-e-services/k3s.tfvars), services map at line 193, landscaping-assistant pattern confirmed at lines 203-210 with matching structure (forgejo_repo, image_repo, port 3000, funnel true, source_repo, source_path). No existing kalshi-assistant entry -- clean for addition.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant but ### Repo section explicitly declares work is in ldraney/pal-e-services. Acceptable pattern for project-specific infrastructure tickets. Single-repo change.

    Dependencies

    Foundational ticket -- "nothing else deploys until this is done." Downstream items on board-kalshi-assistant:

    • #1686 Rails app scaffold (sprint:1) -- blocked, needs namespace
    • #1692 DNS + reverse proxy (sprint:1) -- depends on namespace/funnel
    • #1693 Keycloak realm (sprint:1) -- depends on namespace
    • #1695 Kustomize overlays (sprint:2) -- depends on ArgoCD app
    • #1696 CI/CD pipeline (sprint:2) -- depends on Harbor project

    Acceptance Criteria

    All 5 criteria are agent-verifiable: tofu plan exit 0, tofu apply success, kubectl get ns, Harbor API check, argocd app get. Test command provided: cd ~/pal-e-services && tofu plan -var-file=terraform/k3s.tfvars.

    Blast Radius

    Low. Adds a new entry to the services map without modifying existing entries. Pattern well-established with multiple existing services (believers-elite, landscaping-assistant, mdview, etc.).

    Decomposition Assessment

    No decomposition needed: 1 file target, 1 repo, 5 acceptance criteria (at threshold), estimated agent work under 2 minutes.

    Recommendation

    No action needed. Previous [SCOPE] items resolved:

    • [FIXED] arch-iac note created -- architecture note with Diagram, Components, Key Decisions sections
    • [FIXED] arch-k8s-deploy note created -- architecture note with Diagram, Components, Key Decisions sections
  • Verdict: APPROVED

    Re-review of board item #1686 after refinement. Previous review (review-1686-2026-07-02) found 4 issues. All have been resolved.

    Previous Issues — Resolution

    • [x] arch-rails note — FIXED: note arch-rails ("Rails Architecture: Kalshi Assistant") now exists, status active, project kalshi-assistant
    • [x] arch-api note — FIXED: note arch-api ("API Integration: kalshi-assistant") now exists, status active, project kalshi-assistant
    • [x] Faraday contradiction in Constraints — FIXED: now reads "Use Net::HTTP (stdlib) or Faraday — no other HTTP client gems"
    • [x] Env-dependent AC — FIXED: last AC now includes "(requires API credentials in ~/secrets/kalshi-api-key-id and ~/secrets/kalshi-private-key.key; alternatively verify with a stub/mock if credentials unavailable)"

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage
    • [x] Repo
    • [x] User Story
    • [x] Context
    • [x] File Targets
    • [x] Feature Flag — "none" (acceptable, no feature-flags.md in repo)
    • [x] Acceptance Criteria
    • [x] Test Expectations
    • [x] Constraints
    • [x] Checklist
    • [x] Related

    Traceability

    • [x] story:portfolio-builder label — AI Portfolio Builder
    • [x] story note verified — found in project-kalshi-assistant user-stories section (key: portfolio-builder, links to story-kalshi-assistant-portfolio-builder)
    • [x] arch:rails label — Rails framework component
    • [x] arch note verified — arch-rails note exists in pal-e-docs (active, project kalshi-assistant)
    • [x] arch:api label — Kalshi API integration component
    • [x] arch note verified — arch-api note exists in pal-e-docs (active, project kalshi-assistant)
    • [x] Forgejo issue — ldraney/kalshi-assistant#1, open

    File Targets

    • [x] app/services/kalshi_client.rb — to be created (greenfield scaffold, directory does not yet exist, correct)
    • [x] config/initializers/kalshi.rb — to be created (greenfield scaffold, correct)
    • [x] Gemfile — to be created (greenfield scaffold, correct)
    • [x] docs/api/overview.md — referenced in Context, verified exists in repo
    • [x] docs/api/openapi.yaml — referenced in Context, verified exists in repo

    Note: All target files are "to create" since this is a greenfield Rails scaffold. No existing files will be modified. The docs/ directory is explicitly excluded from modification.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant, Repo section says ldraney/kalshi-assistant. Single repo, consistent.

    Dependencies

    No explicit dependencies documented. This is the first ticket for the project (Lineage: "Standalone"). Other sprint:1 items on the board:

    • #1693 Keycloak realm (sprint:1, type:infra) — parallel, no dependency
    • #1694 Service onboarding — namespace, Harbor, ArgoCD (sprint:1, type:infra) — parallel, not required for scaffold
    • #1692 DNS + reverse proxy (sprint:1, type:infra) — parallel, no dependency

    Sprint:2 item #1688 (Strategy engine — database schema) depends on the Rails scaffold being in place. This is implicit and correctly sequenced via sprint labels.

    Acceptance Criteria

    7 acceptance criteria. All are testable by an agent:

    • "Rails 8 app initialized" — verifiable via file existence and rails --version
    • "Kalshi API client class with RSA-PSS signing" — verifiable via unit test
    • "Support for: GET markets, GET orderbook, GET portfolio/balance, POST orders" — verifiable via method existence
    • "Rate limit awareness" — verifiable via code inspection or unit test
    • "Demo environment config" — verifiable via config file check
    • "API key/secret loaded from environment variables" — verifiable via code inspection
    • "Can run rails console and call KalshiClient.new.balance successfully" — now includes stub/mock fallback if credentials unavailable, testable in all environments

    Blast Radius

    Minimal. Greenfield scaffold, no existing code patterns to check. The docs/ directory is explicitly excluded from modification. No downstream consumers yet.

    Decomposition Assessment

    • File targets: 3 files to create, 1 repo — under threshold
    • Acceptance criteria: 7 — technically exceeds the >5 threshold
    • Estimated agent work: ~5 minutes (Rails scaffold + API client is a single coherent unit)

    No decomposition needed. The 7 AC items are tightly coupled: items 2-7 are all aspects of a single API client built on top of the Rails scaffold (item 1). Decomposing would create artificial dependencies between sub-tickets.

    Recommendation

    No action needed.

  • Verdict: APPROVED

    Re-review after refinement. Previous review (review-1690-2026-07-02) found 3 issues: missing arch-app note, missing dependency on #1, 6 ACs needing consolidation. All three are now resolved.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Depends on #1 (Rails scaffold) and #3 (schema). Both must complete before this sprint:3 ticket.
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- present, well-formed
    • [x] Context -- present, references docs/strategy/sizing-models.md (verified)
    • [x] File Targets -- 2 files to create (app/services/sizing_engine.rb, app/services/scenario_analyzer.rb)
    • [x] Feature Flag -- "none"
    • [x] Acceptance Criteria -- 5 items (consolidated from 6)
    • [x] Test Expectations -- 3 tests + run command (rails test)
    • [x] Constraints -- present (pure Ruby, <1s for 10 picks)
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- story-kalshi-assistant-portfolio-builder exists, listed in project-kalshi-assistant user-stories section
    • [x] arch:app label -- application component
    • [x] arch note verified -- arch-app note exists in pal-e-docs (slug: arch-app, type: architecture, status: active). Contains Mermaid service diagram, component table with SizingEngine entry, and key decisions documenting the 83/17 split.
    • [x] Forgejo issue -- ldraney/kalshi-assistant#5, open

    File Targets

    • [x] app/services/sizing_engine.rb -- to be created. Path depends on Rails scaffold (#1). Dependency is declared in Lineage.
    • [x] app/services/scenario_analyzer.rb -- to be created. Same dependency chain as above.
    • [x] docs/strategy/sizing-models.md -- verified: contains Option D math (83% YES / 17% NO split, -$5.77 worst case, all scenario tables with probabilities and P&L). Context reference is accurate.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant, work targets ldraney/kalshi-assistant. Single repo, no cross-repo concerns.

    Dependencies

    • Declared: #1 (Rails app scaffold, board item #1686, sprint:1, backlog) -- provides the app/services/ directory structure and test runner. Hard blocker.
    • Declared: #3 (Strategy engine -- database schema, board item #1688, sprint:2, backlog) -- provides the strategy/trade schema the sizing engine operates on.
    • Both dependencies are in backlog. Sprint ordering (1 → 2 → 3) enforces the correct sequence.
    • Note: The arch-app diagram shows Watchdog (#1689, issue #4, sprint:3) consumes SizingEngine. That dependency belongs on the Watchdog ticket, not this one.

    Acceptance Criteria

    5 acceptance criteria, all concrete and agent-verifiable:

    • Input contract: picks array with ticker/yes_ask/no_ask + budget -- well-defined interface
    • Optimization method: brute-force over 50-99% YES/NO ratio -- deterministic, reproducible
    • Output contract: optimal ratio, expected value, contracts per position -- complete output spec
    • Scenario table: 2^N outcomes with probability and P&L -- verifiable against docs
    • Scale constraint: 1-10 picks (1024 scenarios max) -- bounded and testable

    Test expectations reference concrete values from docs (83/17, -$5.77) which are verified in sizing-models.md. Run command (rails test) is standard.

    Blast Radius

    Low. Creates new service classes in a greenfield codebase. No existing code to search for similar patterns. No downstream consumers at this stage. SizingEngine is self-contained computation with no external integrations.

    Decomposition Assessment

    • File targets: 2 files in 1 repo -- OK (below >3 across >2 repos threshold)
    • Acceptance criteria: 5 -- at threshold, not over
    • Estimated agent time: under 5 minutes -- math fully documented, two tightly coupled services

    No decomposition needed.

    Previous Review Issues -- Resolution

    Issue Type Status
    arch-app note missing [SCOPE] FIXED -- arch-app note created with service diagram, component table, key decisions
    Missing dependency on #1 in Lineage [BODY] FIXED -- Lineage now reads "Depends on #1 (Rails app scaffold) and #3 (schema)"
    6 ACs exceeding threshold [BODY] FIXED -- Consolidated to 5 by merging "Returns optimal ratio and expected value" into the output AC

    Recommendation

    No action needed. All previous issues resolved. Ticket is ready to move to todo.

  • Verdict: NEEDS_REFINEMENT

    Re-review of board item #1691. Previous review (review-1691-2026-07-02) returned NEEDS_REFINEMENT. Five of six findings were addressed in refinement. One blocker remains: decomposition has not been performed.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- dependencies listed (#1-#5 backend, #10 Keycloak, #4 Watchdog)
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- present
    • [x] Context -- present, includes decomposition recommendation
    • [x] File Targets -- filled (preliminary Rails + Turbo Native paths, marked pending framework spike)
    • [x] Feature Flag -- none
    • [x] Acceptance Criteria -- 6 items
    • [x] Test Expectations -- filled (system tests, Xcode simulator)
    • [x] Constraints -- present
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:app-experience label -- App Experience story
    • [x] story note verified -- found in project-kalshi-assistant user-stories section
    • [x] story:credential-onboarding label -- Credential Onboarding story
    • [x] story note verified -- found in project-kalshi-assistant user-stories section
    • [x] arch:frontend label -- Frontend component
    • [x] arch note verified -- arch-frontend note exists (was MISSING in first review, now FIXED)
    • [x] arch:auth label -- Authentication component
    • [x] arch note verified -- arch-auth note exists (was MISSING in first review, now FIXED)
    • [x] Forgejo issue -- ldraney/kalshi-assistant#6, open

    File Targets

    • [~] app/views/dashboard/ -- does not exist yet; acceptable for Sprint 4 ticket pending Sprint 1 scaffold
    • [~] app/controllers/dashboard_controller.rb -- does not exist yet; same rationale
    • [~] app/views/credentials/ -- does not exist yet; same rationale
    • [~] app/controllers/credentials_controller.rb -- does not exist yet; same rationale
    • [~] ios/ -- does not exist yet; Turbo Native shell, pending framework spike

    All file targets are preliminary and correctly noted as "Pending framework spike." The repo currently contains only docs (no Rails scaffold yet -- that is ticket #1). File targets cannot be verified until Sprint 1 completes. This is acceptable for Sprint 4 scoping but means an agent cannot execute this ticket as-is.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant, which matches ### Repo. The ios/ Turbo Native shell is kept in the same repo -- reasonable for a thin native wrapper.

    Dependencies

    • #1 Rails scaffold (item #1686, backlog) -- prerequisite for all file targets
    • #2 Market scanner (item #1687, backlog) -- provides market data for dashboard
    • #3 Strategy engine (item #1688, backlog) -- provides strategy/trade models
    • #4 Watchdog (item #1689, backlog) -- dashboard needs watchdog data (added in refinement)
    • #5 Option D sizing (item #1690, backlog) -- strategy option
    • #10 Keycloak realm/client (item #1693, backlog) -- auth prerequisite (added in refinement)

    All dependencies documented in Lineage. All are in backlog (Sprint 1-3), correctly blocking this Sprint 4 ticket. FIXED from previous review.

    Acceptance Criteria

    6 criteria spanning 4 distinct feature areas: (1) credential input/validation, (2) strategy + budget configuration, (3) live dashboard, (4) trade history + access revocation. Each criterion is individually testable, but the breadth across 4 domains makes this too large for a single agent pass. Test commands are described at a reasonable level (system tests with mock data, manual Xcode simulator for iOS).

    Blast Radius

    No existing code in the repo to check -- currently docs only. The credential storage and API key handling patterns described here will set security precedent for the project. Encrypted-at-rest requirement in Constraints is critical -- ensure it aligns with arch-auth note's credential storage design.

    Decomposition Assessment

    NEEDS DECOMPOSITION -- route to skill-decompose-ticket.

    • 8 story points
    • 6 acceptance criteria across 4 feature areas
    • 5+ file targets spanning controllers, views, and a native iOS shell
    • Estimated agent work well exceeds 5 minutes
    • The issue Context section itself recommends splitting into 6 sub-tickets

    Suggested decomposition (from issue Context, validated by this review):

    1. Spike: framework decision (React Native / Flutter / Rails Turbo Native)
    2. Credential input UI -- API key + private key with validation
    3. Strategy + budget configuration
    4. Live dashboard -- active positions, unrealized P&L
    5. Trade history log
    6. Access revocation

    Previous Review Findings -- Resolution Status

    • [x] FIXED: arch-frontend note created
    • [x] FIXED: arch-auth note created
    • [x] FIXED: File Targets filled (was TBD)
    • [x] FIXED: Test Expectations filled (was TBD)
    • [x] FIXED: Keycloak dependency #10 added to Lineage
    • [ ] OPEN: Decomposition not performed -- note added to Context acknowledging need, but ticket remains a monolith

    Recommendation

    • [DECOMPOSE] 8 points, 6 AC across 4 feature areas -- route to skill-decompose-ticket. Suggested split: (1) Spike: framework decision, (2) Credential input UI, (3) Strategy + budget config, (4) Live dashboard, (5) Trade history, (6) Access revocation.
  • Verdict: APPROVED

    Re-review after refinement. Previous review review-1687-2026-07-02 found 4 issues (NEEDS_REFINEMENT). All resolved.

    Previous Findings Resolution

    • [x] [SCOPE] arch-api note missing -- RESOLVED: arch-api created (API Integration: kalshi-assistant, architecture type, active, project: kalshi-assistant). Covers RSA-PSS auth, rate limits, endpoint groups.
    • [x] [SCOPE] arch-app note missing -- RESOLVED: arch-app created (Application Domain: Kalshi Assistant, architecture type, active, project: kalshi-assistant). Covers MarketScanner, KalshiClient, Watchdog, SizingEngine components.
    • [x] [LABEL] Arch label mismatch -- RESOLVED: No actual conflict. Board item labels arch:api and arch:app reference component-level notes (what the ticket touches). Story note references arch-domain-kalshi-assistant and arch-dataflow-kalshi-assistant (cross-cutting views). These are complementary layers. arch-app links to both domain and dataflow notes in its Related section.
    • [x] [BODY] Undocumented series tickers -- RESOLVED: Context section now cites source ("Kalshi API exploration, see docs/api/market-data-guide.md for methodology") and clarifies design intent ("scanner should discover active series dynamically rather than hardcoding these"). Ticker list is informational; config/market_series.yml file target provides configurable defaults.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Depends on ldraney/kalshi-assistant #1
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- As the watchdog system / scan same-day markets / detect 85% threshold
    • [x] Context -- Watchdog strategy background, series tickers with source citation, rate limits, dynamic discovery design
    • [x] File Targets -- 4 files to create
    • [x] Feature Flag -- none
    • [x] Acceptance Criteria -- 6 items
    • [x] Test Expectations -- unit + integration tests, run command
    • [x] Constraints -- Solid Queue, rate limits, 24h history
    • [x] Checklist -- PR, tests, no unrelated changes
    • [x] Related -- project reference

    Traceability

    • [x] story:watchdog-trading label -- Watchdog Trading
    • [x] story note verified -- story-kalshi-assistant-watchdog-trading exists (active, user-story type)
    • [x] story entry verified -- found in project-kalshi-assistant user-stories table (row: watchdog-trading, role: Trader)
    • [x] arch:api label -- Kalshi API integration component
    • [x] arch note verified -- arch-api exists (active, architecture type, project: kalshi-assistant)
    • [x] arch:app label -- Application domain component
    • [x] arch note verified -- arch-app exists (active, architecture type, project: kalshi-assistant)
    • [x] Forgejo issue -- ldraney/kalshi-assistant#2, open

    File Targets

    • [x] app/jobs/market_scanner_job.rb -- to create (Solid Queue job). Depends on #1 (Rails scaffold).
    • [x] app/models/market_snapshot.rb -- to create (snapshot model). Depends on #1.
    • [x] db/migrate/xxx_create_market_snapshots.rb -- to create (migration). Depends on #1.
    • [x] config/market_series.yml -- to create (configurable series list). Depends on #1.

    All file targets are files to CREATE. Paths follow Rails conventions. Repo currently has no Rails structure (only docs). Valid assuming #1 completes first.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant, Repo section says ldraney/kalshi-assistant. Single repo, no multi-repo concern.

    Dependencies

    • Upstream: Depends on issue #1 (Rails scaffold + API client) -- documented in Lineage. Board item #1686 in backlog (sprint:1). This ticket is sprint:2. Correct ordering.
    • Downstream: Board item #1689 "Watchdog -- auto-buy when market crosses 85% threshold" (sprint:3, issue #4) logically depends on this scanner. Correct sprint order.
    • Parallel: Board item #1688 "Strategy engine -- database schema" (sprint:2, issue #3) is in the same sprint. No conflict -- can run in parallel once #1 completes.

    Acceptance Criteria

    6 criteria, all agent-testable:

    • [x] Background job polls -- verifiable via test that job enqueues and runs
    • [x] Scans configured series -- verifiable via config iteration test
    • [x] Stores snapshots with specified fields -- verifiable via DB assertions
    • [x] Filters same-day/next-day -- verifiable via date filtering test
    • [x] Respects rate limits -- verifiable via configurable delay check
    • [x] Logs 85% threshold crossing -- verifiable via log output assertion

    Blast Radius

    Minimal. New repo with only docs and README -- no existing production code. Referenced docs confirmed present: docs/api/market-data-guide.md and docs/strategy/watchdog-strategy.md both exist in repo. Architecture notes arch-api and arch-app document the components this ticket creates.

    Decomposition Assessment

    • 4 file targets across 1 repo -- under the >3 files across >2 repos threshold.
    • 6 acceptance criteria -- borderline exceeds the >5 threshold by 1.
    • Estimated agent work: ~5 minutes. Creating 4 tightly coupled files for a single scanner feature.

    No decomposition needed. The 6 AC are all aspects of a single coherent scanner component. Splitting would create artificially coupled sub-tickets.

    Recommendation

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

  • Verdict: APPROVED

    Re-review of board item #1688 after refinement. Previous review (review-1688-2026-07-02) found two missing architecture backing notes. Both have been created and verified.

    Previous Findings -- Resolution

    • [x] [SCOPE] Create architecture note arch-rails -- FIXED. arch-rails exists (note_type=architecture, status=active, project=kalshi-assistant). Contains Rails 8 component diagram, service objects, Solid Queue/Cable decisions.
    • [x] [SCOPE] Create architecture note arch-postgres -- FIXED. arch-postgres exists (note_type=architecture, status=active, project=kalshi-assistant). Contains ER diagram with strategies, trades, markets, portfolios. Includes CNPG and jsonb strategy polymorphism decisions.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Depends on ldraney/kalshi-assistant #1 (Rails scaffold)
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- As a developer / I want a database schema / So that strategies are data-driven
    • [x] Context -- Strategy explanation with doc references
    • [x] File Targets -- 6 files to create
    • [x] Feature Flag -- none
    • [x] Acceptance Criteria -- 6 criteria
    • [x] Test Expectations -- Unit tests, rails test command
    • [x] Constraints -- 3 constraints listed
    • [x] Checklist -- PR/tests/no unrelated changes
    • [x] Related -- kalshi-assistant project

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- found in project-kalshi-assistant user-stories section (key: portfolio-builder, links to story-kalshi-assistant-portfolio-builder)
    • [x] arch:rails label -- Rails framework
    • [x] arch note verified -- arch-rails note exists in pal-e-docs (note_type=architecture, status=active, project=kalshi-assistant)
    • [x] arch:postgres label -- PostgreSQL database
    • [x] arch note verified -- arch-postgres note exists in pal-e-docs (note_type=architecture, status=active, project=kalshi-assistant)
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/kalshi-assistant/issues/3, open

    File Targets

    All file targets are files to CREATE. The repo is currently docs-only because this ticket depends on #1 (Rails scaffold, sprint:1, backlog). File targets cannot be verified against existing code but are standard Rails conventions.

    • [x] app/models/strategy.rb -- to create: name, thresholds, allocation percentages
    • [x] app/models/pick.rb -- to create: belongs_to strategy, market ticker, implied_prob
    • [x] app/models/trade.rb -- to create: belongs_to pick, side, contracts, price, status
    • [x] app/models/outcome.rb -- to create: settlement result, P&L
    • [x] db/migrate/ -- to create: all migrations
    • [x] db/seeds.rb -- to create: seed Option D and Watchdog strategies

    Repo Placement

    OK. Issue #3 is filed on ldraney/kalshi-assistant and all work targets that same repo. Single repo, no cross-repo concerns.

    Dependencies

    • Documented: Depends on #1 (Rails scaffold, item #1686, sprint:1, backlog). Correctly sequenced -- this ticket is sprint:2.
    • Downstream consumers: #1689 Watchdog (sprint:3) and #1690 Option D sizing engine (sprint:3) both build on models created here. #1687 Market scanner (sprint:2) may interact with Pick model for storing scan results.
    • Dependencies are properly documented in the Lineage section.

    Acceptance Criteria

    6 criteria, all agent-verifiable:

    • Strategy/Pick/Trade/Outcome model fields -- verifiable via schema inspection and model attribute checks
    • Seeds -- verifiable via rails runner or db:seed + query
    • Strategy.find_by(name: 'watchdog').confidence_threshold returns 0.85 -- very specific, directly testable

    Values verified against docs:

    • 83/17 split: confirmed in docs/strategy/sizing-models.md ("83% YES / 17% NO hedge on all picks")
    • 0.85 confidence threshold: confirmed in docs/strategy/core-strategy.md ("85%+ implied probability")
    • Watchdog strategy: docs/strategy/watchdog-strategy.md exists in the repo

    Blast Radius

    Minimal. Greenfield project -- no existing code to break. Models created here become the foundation for sprint 3 tickets (watchdog, sizing engine). No external services consumed by this ticket directly.

    Decomposition Assessment

    6 file targets in 1 repo. 6 acceptance criteria (marginally over 5 threshold). However, this is standard Rails model/migration/seed work -- well under 5 minutes for an agent. All targets are in one repo and belong to a single domain (data models). No decomposition needed.

    Recommendation

    No action needed. All previous findings resolved.

  • Verdict: APPROVED

    Re-review of board item #1697. Previous review (review-1697-2026-07-02) returned NEEDS_REFINEMENT with 3 issues. All 3 have been resolved.

    Previous Issues -- Resolution Status

    # Tag Issue Status
    1 [SCOPE] Create architecture note arch-observability FIXED -- note exists (id 2344), comprehensive content with Mermaid diagram, component table, key decisions, and related links. Project: kalshi-assistant.
    2 [BODY] Add dependencies to issue body FIXED -- Lineage section updated: "Depends on #7 (service onboarding), #9 (kustomize), #1 (Rails app /up endpoint)."
    3 [BODY] Add cross-repo note FIXED -- Callout block added: "Cross-repo: All code changes target ldraney/pal-e-platform. PR targets pal-e-platform, not this repo."

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Depends on #7, #9, #1 (updated from "Standalone")
    • [x] Repo -- ldraney/pal-e-platform
    • [x] User Story
    • [x] Context -- includes cross-repo callout
    • [x] File Targets -- 2 targets (1 modify, 1 create)
    • [x] Feature Flag -- none
    • [x] Acceptance Criteria -- 4 items
    • [x] Test Expectations -- tofu plan
    • [x] Constraints -- pattern matching, Telegram receiver naming, dashboard JSON format
    • [x] Checklist
    • [x] Related

    Traceability

    • [x] story:platform-setup label -- Platform Setup
    • [x] story note verified -- found in project-kalshi-assistant user-stories section (key: platform-setup, role: Developer, metric: CI/CD push-to-deploy in under 10 minutes)
    • [x] arch:observability label -- observability component
    • [x] arch note verified -- arch-observability note exists in pal-e-docs (id 2344, project kalshi-assistant). Includes Mermaid diagram, component table (ServiceMonitor, PrometheusRules, AlertManager, blackbox-exporter, Grafana dashboards), key decisions, and related links.
    • [x] Forgejo issue -- https://forgejo.tail5b443a.ts.net/ldraney/kalshi-assistant/issues/12, open
    • [x] arch-deployment-kalshi-assistant (mentioned in Related) -- verified, note exists (id 2327)

    File Targets

    • [x] terraform/modules/monitoring/main.tf -- verified: exists (28k), landscaping-assistant pattern confirmed (AlertManager route/receiver, blackbox probe, dashboard ConfigMap, PrometheusRule). No kalshi-assistant content yet (work not started).
    • [x] terraform/dashboards/kalshi-assistant-golden-signals.json -- to create: dashboards directory exists with 8 existing dashboards including landscaping-assistant-golden-signals.json as the template to follow.

    Repo Placement

    Cross-repo filing is now explicitly documented. Issue filed on ldraney/kalshi-assistant, code changes target ldraney/pal-e-platform. The ### Repo field and the new cross-repo callout make this unambiguous for implementing agents. Single repo affected (pal-e-platform), so no multi-issue coordination needed.

    Dependencies

    All dependencies now documented in Lineage section:

    • #7 / board #1694 (Service onboarding -- namespace, Harbor, ArgoCD, Tailscale funnel) -- sprint:1. Namespace must exist.
    • #9 / board #1695 (Kustomize deployment overlays -- prod and dev) -- sprint:2. ServiceMonitor provisioned here.
    • #1 / board #1686 (Rails app scaffold with Kalshi API client) -- sprint:1. Provides /up health endpoint for blackbox probe.

    Sprint:3 placement is correct given all 3 dependencies are sprint:1 or sprint:2. All dependencies are on the same board (board-kalshi-assistant), currently in backlog.

    Acceptance Criteria

    4 criteria, all agent-verifiable:

    • AlertManager routing -- verifiable via tofu plan output
    • Blackbox probe endpoint -- verifiable via tofu plan; runtime verification requires deployed app
    • Grafana dashboard golden signals -- verifiable by checking ConfigMap creation in plan
    • PrometheusRule thresholds -- verifiable via tofu plan checking rule expressions

    All criteria are well-scoped and measurable. Runtime verification depends on upstream deployments but infrastructure-level verification is self-contained.

    Blast Radius

    Low. Changes follow the established landscaping-assistant pattern exactly. All modifications are additive:

    • New AlertManager route and receiver (append to existing arrays)
    • New blackbox probe target (append to existing targets)
    • New dashboard JSON file (no modifications to existing dashboards)
    • New PrometheusRule kubernetes_manifest (no modifications to existing rules)

    No downstream consumers affected.

    Decomposition Assessment

    No decomposition needed:

    • 2 file targets in 1 repo -- under threshold
    • 4 acceptance criteria -- under threshold
    • Estimated agent work: 3-5 minutes (pattern copy from landscaping-assistant, adapt names/namespaces) -- within 5-minute rule

    Recommendation

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

  • Verdict: APPROVED

    Re-review of board item #1689. Previous review (review-1689-2026-07-02) returned NEEDS_REFINEMENT with four findings. All have been addressed.

    Previous Findings Resolution

    • [x] [BODY] Lineage missing #1 -- RESOLVED. Lineage now reads: "Depends on ldraney/kalshi-assistant #1 (Rails app scaffold), #2 (scanner), and #3 (schema). The app/ directory doesn't exist yet -- it's created by #1."
    • [x] [LABEL] Arch label mismatch -- RESOLVED. Rather than realigning labels, backing architecture notes were created to match the existing arch:app and arch:api labels.
    • [x] [SCOPE] Create architecture notes -- RESOLVED. arch-app (Application Domain: Kalshi Assistant) created with Mermaid diagram, component table, and key decisions. arch-api (API Integration: kalshi-assistant) created with sequence diagram, endpoints, rate limits, and auth flow.
    • [x] [DECOMPOSE] 6 AC, extract OrderService -- ACCEPTED AS BORDERLINE. Context section now acknowledges OrderService extraction option: "Consider extracting a shared OrderService... either as part of this ticket or split into a foundational ticket." AC count remains 6 (1 over threshold), but this is acceptable: single repo, 3 tightly-coupled files, ~5 min estimated work.

    Template Completeness

    Issue type: Feature (template-issue-feature)

    • [x] Type -- "Feature"
    • [x] Lineage -- documents deps on #1 (scaffold), #2 (scanner), #3 (schema)
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- present, well-formed
    • [x] Context -- references watchdog-strategy.md (verified exists), API endpoint, OrderService note
    • [x] File Targets -- 3 CREATE targets listed
    • [x] Feature Flag -- watchdog_live_trading, global, disabled by default
    • [x] Acceptance Criteria -- 6 criteria listed, all testable
    • [x] Test Expectations -- unit tests + run command
    • [x] Constraints -- dry-run default, logging, error handling
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:watchdog-trading label -- Watchdog Trading
    • [x] story note verified -- story-kalshi-assistant-watchdog-trading exists (active, user-story); entry found in project-kalshi-assistant user-stories table with success metric "85%+ win rate, $0.10-$0.15 avg profit per contract"
    • [x] arch:app label -- Application Domain
    • [x] arch note verified -- arch-app note exists (active, architecture); includes Mermaid diagram showing Watchdog component, service object table, key decisions
    • [x] arch:api label -- API Integration
    • [x] arch note verified -- arch-api note exists (active, architecture); includes sequence diagram, endpoint groups, rate limit table, RSA-PSS auth flow
    • [x] Forgejo issue -- ldraney/kalshi-assistant#4, state: open

    File Targets

    • [x] app/services/watchdog_service.rb -- CREATE. Requires #1 (Rails scaffold) for app/ directory. Dependency documented in Lineage.
    • [x] app/jobs/watchdog_job.rb -- CREATE. Same #1 dependency. Solid Queue job pattern documented in arch-app and arch-rails notes.
    • [x] app/services/order_service.rb -- CREATE. Shared Kalshi API order client. Reuse by #5 (Option D sizing) acknowledged in Context.

    Repo Placement

    OK. Single repo (ldraney/kalshi-assistant), all targets in same repo. Issue filed on correct repo.

    Dependencies

    • #1 Rails app scaffold (sprint:1, backlog) -- app/ directory structure. NOW DOCUMENTED in Lineage.
    • #2 Market scanner (sprint:2, backlog) -- scanner triggers watchdog. Documented.
    • #3 Strategy engine schema (sprint:2, backlog) -- Trade/Pick/Strategy models. Documented.
    • Sprint ordering: Correct -- this is sprint:3, all deps are sprint:1-2.
    • Downstream: #5 (Option D sizing, sprint:3) may share order_service.rb -- acknowledged in Context.
    • Board state: All 12 items remain in backlog. No blockers in_progress.

    Acceptance Criteria

    6 AC, all verifiable by an agent:

    • [x] Threshold detection (yes_bid >= confidence_threshold) -- unit testable, aligns with watchdog-strategy.md 85%+ entry
    • [x] API order placement (POST /portfolio/events/orders, side: bid) -- confirmed in openapi.yaml line 1143; integration testable with HTTP mocks
    • [x] Trade record with order_id -- DB assertion testable
    • [x] Sell triggers 95%+ / below 80% -- unit testable, matches watchdog-strategy.md sell triggers table
    • [x] Dry-run mode -- unit testable with feature flag mock
    • [x] Pick and Trade records -- DB assertion testable

    Blast Radius

    Greenfield project -- no existing application code. All file targets are CREATE. order_service.rb is a shared candidate for #5 but that creates no conflict since #5 is also greenfield.

    Decomposition Assessment

    • 3 file targets in 1 repo -- OK (under threshold)
    • 6 acceptance criteria -- borderline (1 over >5 threshold)
    • Estimated agent work -- ~5 minutes, borderline
    • OrderService extraction acknowledged as option in Context but kept in this ticket for cohesion

    No decomposition needed. Borderline case accepted: tightly coupled domain logic, single repo, and Context explicitly flags the extraction option for future consideration.

    Observations (non-blocking)

    • arch-api key endpoints table uses legacy endpoint path (/portfolio/orders) while issue body and openapi.yaml use the newer /portfolio/events/orders. Should be corrected in the arch note separately.
    • Feature flag section filled despite no docs/feature-flags.md in repo. Acceptable for greenfield; the doc will be established as part of the Rails scaffold (#1).

    Recommendation

    No action needed. All previous findings resolved. Ticket is ready for implementation once sprint:1 and sprint:2 dependencies are complete.

  • Verdict: APPROVED

    Re-review of board item #1695 after refinement. All 6 issues from the initial review (review-1695-2026-07-02) have been addressed. Scope is solid, traceability complete, file targets verified against the actual landscaping-assistant reference pattern.

    Previous Findings -- Resolution

    • [x] Dev overlay file targets incomplete -- FIXED: 6 dev files added (configmap.yaml, ingress.yaml, namespace.yaml, service.yaml plus the 2 originals), matching landscaping-assistant pattern exactly
    • [x] Prod rename patches undocumented -- FIXED: kustomization.yaml description now includes "inline rename patches (Deployment, Service, ServiceMonitor from 'app' to 'kalshi-assistant')"
    • [x] Dependencies undocumented -- FIXED: Lineage changed from "Standalone" to "Depends on #7 (service onboarding -- creates namespace and ArgoCD app), #1 (Rails scaffold -- produces container image)"
    • [x] No dev-specific acceptance criteria -- FIXED: 3 dev ACs added (kalshi-dev namespace, nginx proxy routes, kustomize build dev succeeds)
    • [x] Architecture notes missing -- FIXED: arch-kustomize and arch-k8s-deploy notes created in pal-e-docs, both active, project kalshi-assistant
    • [x] Cross-repo note missing -- FIXED: callout added "PR targets ldraney/pal-e-deployments, not this repo"

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Depends on #7, #1
    • [x] Repo -- ldraney/pal-e-deployments
    • [x] User Story -- present
    • [x] Context -- present, references landscaping-assistant model
    • [x] File Targets -- 9 files (3 prod, 6 dev)
    • [x] Feature Flag -- none
    • [x] Acceptance Criteria -- 9 criteria
    • [x] Test Expectations -- kustomize build command provided
    • [x] Constraints -- pattern, namespace, port, secrets documented
    • [x] Checklist -- PR, build, SOPS, no unrelated changes
    • [x] Related -- project and arch note referenced

    Traceability

    • [x] story:platform-setup label -- verified in project-kalshi-assistant user-stories table
    • [x] story note verified -- "Platform Setup" entry exists with link to story-kalshi-assistant-platform-setup
    • [x] arch:kustomize label -- arch-kustomize note exists (active, project kalshi-assistant)
    • [x] arch:k8s-deploy label -- arch-k8s-deploy note exists (active, project kalshi-assistant)
    • [x] Forgejo issue -- ldraney/kalshi-assistant#9, state: open

    File Targets

    • [x] overlays/kalshi-assistant/prod/kustomization.yaml -- to create; matches landscaping-assistant/prod/kustomization.yaml
    • [x] overlays/kalshi-assistant/prod/deployment-patch.yaml -- to create; matches landscaping-assistant/prod/deployment-patch.yaml
    • [x] overlays/kalshi-assistant/prod/secrets.enc.yaml -- to create; matches landscaping-assistant/prod/secrets.enc.yaml
    • [x] overlays/kalshi-assistant/dev/kustomization.yaml -- to create; matches landscaping-assistant/dev/kustomization.yaml
    • [x] overlays/kalshi-assistant/dev/deployment.yaml -- to create; matches landscaping-assistant/dev/deployment.yaml
    • [x] overlays/kalshi-assistant/dev/configmap.yaml -- to create; matches landscaping-assistant/dev/configmap.yaml
    • [x] overlays/kalshi-assistant/dev/ingress.yaml -- to create; matches landscaping-assistant/dev/ingress.yaml
    • [x] overlays/kalshi-assistant/dev/namespace.yaml -- to create; matches landscaping-assistant/dev/namespace.yaml
    • [x] overlays/kalshi-assistant/dev/service.yaml -- to create; matches landscaping-assistant/dev/service.yaml

    All 9 file targets verified against the actual landscaping-assistant overlay in pal-e-deployments. Pattern match is exact (3 prod files, 6 dev files, identical names). No kalshi-assistant overlay exists yet (expected).

    Repo Placement

    Issue filed on ldraney/kalshi-assistant, work targets ldraney/pal-e-deployments. Cross-repo callout clearly documented in issue body. Implementing agent must PR against pal-e-deployments. Acceptable -- issue is scoped to the project, code lives in the deployment repo.

    Dependencies

    • #7 Service onboarding (board item #1694, sprint:1) -- creates namespace, Harbor project, ArgoCD app. Must complete before this ticket. Currently in backlog. Documented in Lineage.
    • #1 Rails app scaffold (board item #1686, sprint:1) -- produces the container image that prod overlay deploys. Must complete before this ticket. Currently in backlog. Documented in Lineage.

    Sprint ordering is consistent: dependencies are sprint:1, this ticket is sprint:2.

    Acceptance Criteria

    9 criteria, all agent-verifiable:

    • [x] kustomize build prod -- testable via provided command
    • [x] SOPS encryption -- verifiable with sops decrypt
    • [x] Init container db:prepare -- verifiable in YAML
    • [x] Liveness/readiness probes -- verifiable in YAML
    • [x] Resource limits -- verifiable in YAML
    • [x] Security context -- verifiable in YAML
    • [x] Dev namespace -- verifiable via kustomize build
    • [x] Dev nginx proxy -- verifiable in YAML
    • [x] kustomize build dev -- testable via command

    Test command provided: cd ~/pal-e-deployments && kustomize build overlays/kalshi-assistant/prod. All criteria are concrete and machine-verifiable.

    Blast Radius

    Low. Creates new files only in a new overlay directory. No existing services or overlays are modified. No shared base files are touched.

    Decomposition Assessment

    9 acceptance criteria exceeds the >5 threshold. However, all files are in one repo, follow an established pattern (landscaping-assistant), and the work is mechanical pattern-copy with service-specific values. Estimated agent time: 3-5 minutes. No decomposition needed.

    Note

    The arch-kustomize note's diagram shows a slightly different file structure (base/ directory, rename-patches.yaml, servicemonitor.yaml) than what the actual landscaping-assistant pattern and this issue's file targets use. The issue spec correctly follows the real pattern. The arch note may warrant a minor update to align with the actual implementation, but this does not affect the ticket scope.

    Recommendation

    No action needed. All previous findings resolved. Ticket is ready for implementation.

  • Verdict: APPROVED

    Scope is solid. All file targets verified against pal-e-platform codebase. Template complete. 2 file targets in 1 repo, 4 acceptance criteria — well within the 5-minute rule.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Standalone
    • [x] Repo — ldraney/pal-e-platform
    • [x] User Story — present
    • [x] Context — present, references landscaping-assistant.app model
    • [x] File Targets — 2 files to modify, 2 exclusions documented
    • [x] Feature Flag — none (infrastructure, appropriate)
    • [x] Acceptance Criteria — 4 criteria
    • [x] Test Expectations — 3 items with run command
    • [x] Constraints — 3 pattern constraints documented
    • [x] Checklist — present
    • [x] Related — present

    Traceability

    • [x] story:app-experience label — App Experience
    • [x] story note verified — found in project-kalshi-assistant user-stories section (role: Consumer)
    • [x] arch:edge-proxy label — edge proxy component
    • [ ] arch note arch-edge-proxy — not found in pal-e-docs. Shared platform component; acceptable for infra work, not blocking.
    • [x] arch:iac label — infrastructure-as-code component
    • [ ] arch note arch-iac — not found in pal-e-docs. Shared platform component; acceptable for infra work, not blocking.
    • [x] Forgejo issue — ldraney/kalshi-assistant#8, open

    File Targets

    • [x] terraform/dns.tf — verified: exists in pal-e-platform, contains landscaping_assistant_a resource pattern at line 12
    • [x] salt/pillar/caddy.sls — verified: exists in pal-e-platform, contains landscaping site entry at line 17 with domain, proxy_target, www_redirect fields

    Repo Placement

    Issue filed on ldraney/kalshi-assistant, work happens in ldraney/pal-e-platform. This is the standard pattern for platform-level infrastructure work tracked in the project repo. OK.

    Dependencies

    No blocking dependencies. Service onboarding (#1694, sprint:1) is a related item but can proceed independently — DNS and reverse proxy do not depend on namespace or ArgoCD setup. The issue correctly notes that Keycloak theme and monitoring are separate tickets.

    Acceptance Criteria

    4 criteria, all verifiable by an agent. tofu plan is a real command. dig and curl -I are standard verification commands. The expected 502 response (no app deployed yet) is a thoughtful criterion that prevents false positives. Test expectations align with acceptance criteria.

    Blast Radius

    Minimal. Adding a new GoDaddy A record and Caddy site entry. Pattern follows existing landscaping-assistant.app model. No existing DNS records or Caddy configs are modified. No downstream consumers affected — the domain currently has no services behind it.

    Decomposition Assessment

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

    Recommendation

    No action needed. Scope is complete and well-structured.

    Minor advisory (non-blocking): arch notes arch-edge-proxy and arch-iac do not exist in pal-e-docs. These are shared platform components used across multiple projects. Consider creating them as shared architecture notes when convenient.

  • Verdict: APPROVED

    CI/CD pipeline ticket is well-scoped, all template sections complete, traceability solid. Minor recommendation to create the backing architecture note for arch:ci-pipeline.

    Template Completeness

    • [x] Type — Feature
    • [x] Lineage — Standalone
    • [x] Repo — ldraney/kalshi-assistant
    • [x] User Story — present
    • [x] Context — present, references landscaping-assistant pattern
    • [x] File Targets — 2 files to create
    • [x] Feature Flag — none (explicitly stated)
    • [x] Acceptance Criteria — 6 criteria
    • [x] Test Expectations — present
    • [x] Constraints — present with Harbor base image and target
    • [x] Checklist — present
    • [x] Related — present

    Traceability

    • [x] story:platform-setup label — Platform Setup
    • [x] story note verified — found in project-kalshi-assistant user-stories section
    • [x] arch:ci-pipeline label — CI Pipeline
    • [ ] arch note MISSING — [SCOPE] Create architecture note arch-ci-pipeline for component ci-pipeline
    • [x] Forgejo issue — ldraney/kalshi-assistant#11, open

    File Targets

    • [x] .woodpecker.yaml — to create; confirmed does not exist yet
    • [x] Dockerfile — to create (if not created by rails scaffold ticket); confirmed does not exist yet

    Repo Placement

    OK — issue filed on ldraney/kalshi-assistant, all work targets the same repo.

    Dependencies

    • Board item #1694 "Service onboarding — namespace, Harbor, ArgoCD, Tailscale funnel" (sprint:1, todo) — upstream dependency. Harbor registry and ArgoCD must exist before CI can push images and trigger deploys.
    • Board item #1686 "Rails app scaffold with Kalshi API client" (sprint:1, todo) — upstream dependency. Dockerfile and CI steps require a Rails app to build.
    • Board item #1695 "Kustomize deployment overlays" (sprint:2, backlog) — related but not a hard blocker for CI pipeline creation.

    Dependencies are implicit in sprint ordering (sprint:1 items before sprint:2) but not explicitly documented in the issue scope. Acceptable for infra work where sprint ordering conveys dependency.

    Acceptance Criteria

    6 criteria, all agent-verifiable: pipeline trigger behavior (push to any branch vs main), RuboCop lint pass, RSpec with Postgres service, Kaniko image build+push, ArgoCD Image Updater pickup. Test expectations include manual push trigger and Harbor image verification. Solid and testable.

    Blast Radius

    Minimal. Repo is currently bare (only README.md and docs/). No existing CI config, no Dockerfile, no conflicting patterns. The .woodpecker.yaml and Dockerfile are new additions with no downstream consumers yet.

    Decomposition Assessment

    2 file targets across 1 repo. 6 acceptance criteria (at threshold but within bounds). Estimated agent work well under 5 minutes — straightforward CI config following an established pattern (landscaping-assistant). No decomposition needed.

    Recommendation

    • [SCOPE] Create architecture note arch-ci-pipeline for the ci-pipeline component. Non-blocking — the work is well-defined without it, but traceability would be complete with the note.

    No other action needed. Ticket is ready for implementation.

  • Verdict: APPROVED

    Re-review of board item #1693. All 7 issues from the previous NEEDS_REFINEMENT review have been verified fixed. One new documentation issue found in arch notes (non-blocking). Ticket is ready for implementation.

    Previous Review Fixes (all verified)

    • [x] Theme file paths corrected from keycloak-theme/ to keycloak/themes/kalshi-assistant/
    • [x] ConfigMap reference removed (themes are directly mounted, no ConfigMap pattern exists)
    • [x] AC #6 replaced: "OmniAuth callback works" (unachievable) changed to "Keycloak client configured with correct redirect URI for future Rails OmniAuth integration (verified in Keycloak admin console)"
    • [x] Dependencies added to Lineage: depends on #7 (service onboarding), soft dep on #1 (Rails scaffold)
    • [x] Cross-Repo Note section added explaining PRs target pal-e-services and pal-e-platform, not kalshi-assistant
    • [x] Repo description cleaned: "theme ConfigMap" changed to "theme files"
    • [x] Architecture note arch-keycloak-kalshi-assistant created
    • [x] Architecture note arch-auth created

    Template Completeness

    Validated against template-issue-feature.

    • [x] Type — Feature
    • [x] Lineage — depends on #7, notes #1
    • [x] Repo — ldraney/pal-e-services + ldraney/pal-e-platform
    • [x] Cross-Repo Note — bonus section, clarifies PR targeting
    • [x] User Story — login with Keycloak SSO
    • [x] Context — follows landscaping-assistant model, Auth Code + PKCE
    • [x] File Targets — 3 files across 2 repos
    • [x] Feature Flag — none
    • [x] Acceptance Criteria — 6 items
    • [x] Test Expectations — tofu plan + manual test
    • [x] Constraints — landscaping pattern, registration, PKCE, no direct grants
    • [x] Checklist — PR, tofu plan, theme, no unrelated changes
    • [x] Related — project, story, SOP references

    Traceability

    • [x] story:app-experience label — App Experience
    • [x] story note verified — "app-experience" found in project-kalshi-assistant user-stories section
    • [x] arch:keycloak label — Keycloak configuration
    • [x] arch note verified — arch-keycloak-kalshi-assistant exists in pal-e-docs (note: slug uses -kalshi-assistant suffix because arch-keycloak was taken)
    • [x] arch:auth label — Authentication architecture
    • [x] arch note verified — arch-auth exists in pal-e-docs
    • [x] Forgejo issue — ldraney/kalshi-assistant#10, open

    File Targets

    • [x] terraform/k3s.tfvars (pal-e-services) — verified: file exists (symlink to secrets), keycloak_realms at line 40, keycloak_clients at line 92, keycloak_users at line 292. Landscaping-assistant pattern at lines 139-166 confirmed as reference.
    • [x] keycloak/themes/kalshi-assistant/login/theme.properties (pal-e-platform) — to create. Matches convention: keycloak/themes/landscaping/login/theme.properties exists.
    • [x] keycloak/themes/kalshi-assistant/login/resources/css/login.css (pal-e-platform) — to create. Matches convention: keycloak/themes/landscaping/login/resources/css/login.css exists.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant (the project repo), but work targets ldraney/pal-e-services (Terraform config) and ldraney/pal-e-platform (theme files). Cross-Repo Note explicitly documents this. PRs should target those repos. This is acceptable for project-level infrastructure work.

    Dependencies

    • #7 (Service onboarding — namespace, Harbor, ArgoCD, Tailscale funnel) — board item 1694, sprint:1, backlog. Hard dependency: namespace and ArgoCD app must exist first. Documented in Lineage.
    • #1 (Rails app scaffold with Kalshi API client) — board item 1686, sprint:1, backlog. Soft dependency: needed for full OAuth flow testing only. Documented in Lineage.

    Acceptance Criteria

    6 criteria, all testable:

    • AC 1-4: verifiable via tofu plan (realm, client, redirect URIs, seeded user)
    • AC 5: manual verification (login page branding)
    • AC 6: verifiable in Keycloak admin console (redirect URI configuration)

    All criteria are achievable within this ticket's scope (AC #6 was correctly scoped down from the previous "OmniAuth callback works" version).

    Blast Radius

    Low. All changes are additive:

    • k3s.tfvars: adding new realm/client/user entries alongside existing ones. No modifications to landscaping, westside-basketball, or platform realms.
    • Theme files: new directory under keycloak/themes/. No changes to existing themes.

    Decomposition Assessment

    3 file targets across 2 repos (does not exceed >3 across >2 threshold). 6 AC (borderline at >5 threshold, but work is cohesive: one Terraform block + two theme files). Estimated agent time ~3-4 minutes. No decomposition needed.

    New Finding: Arch Notes Contradict Issue Spec

    Both arch-keycloak-kalshi-assistant and arch-auth describe ROPC/direct grant with a public client, but the issue spec and the verified landscaping-assistant pattern (k3s.tfvars lines 139-166) both specify Auth Code + PKCE with a confidential client and direct_access_grants_enabled = false.

    Specific contradictions:

    • arch-keycloak-kalshi-assistant: diagram shows IOS -->|"direct grant"| CLIENT, components table says "Direct grant enabled. Public client", key decisions say "Direct grant for mobile — Public client with ROPC"
    • arch-auth: diagram shows App->>KC: Direct grant (username + password), components table says "Direct grant flow (no browser redirect)", key decisions say "Direct grant over redirect flow"

    The issue spec is correct (Auth Code + PKCE, confidential, no ROPC). The arch notes should be updated to match. This is non-blocking because the Forgejo issue is the implementation authority, and it is correct.

    Recommendation

    • [SCOPE] Update arch-keycloak-kalshi-assistant: change "direct grant / public client / ROPC" references to "Auth Code + PKCE / confidential client / standard flow". Fix diagram, components table, and key decisions section.
    • [SCOPE] Update arch-auth: change "direct grant (username + password)" to "Auth Code + PKCE via ASWebAuthenticationSession". Fix diagram, components table, and key decisions section.
  • Verdict: READY

    Template Completeness

    Validated against template-issue-spike.

    • [x] Type — Spike
    • [x] Lineage — Related to ldraney/kalshi-assistant #6 (parent issue). Decision gate for all mobile app work in sprint 4.
    • [x] Repo — ldraney/kalshi-assistant
    • [x] Question — Main question with 5 sub-questions covering Rails+Turbo Native+Capacitor, PWA, React Native/Flutter, secure storage, and developer velocity
    • [x] Deliverables — 3 deliverables: ADR doc, follow-up ticket updates, decision record
    • [x] Time-box — 1 session, with escalation path
    • [x] Related — Project and parent issue linked

    Traceability

    • [x] story:app-experience label — App Experience
    • [x] story note verified — story-kalshi-assistant-app-experience found in project-kalshi-assistant user-stories section
    • [x] arch:frontend label — Frontend
    • [x] arch note verified — arch-frontend note exists in pal-e-docs (title: "Frontend: kalshi-assistant", status: active)
    • [x] Forgejo issue — ldraney/kalshi-assistant #13, open

    File Targets

    • [x] docs/architecture/mobile-framework-adr.md — target deliverable. Directory docs/architecture/ does not exist yet; will be created as part of the spike. Parent docs/ exists. Appropriate for a spike that produces docs.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant, matches ### Repo field. Single-repo investigation.

    Dependencies

    • Parent: #1691 (issue #6) "Mobile app — API key connection and strategy dashboard" (backlog, sprint 4, 8pts) — documented in Lineage
    • Downstream sprint 4 frontend items blocked by this decision:
      • #1699 (issue #14) "Credential input and management UI" (backlog, 2pts)
      • #1700 (issue #15) "Strategy and budget configuration UI" (backlog, 1pt)
      • #1701 (issue #16) "Live strategy dashboard" (backlog, 2pts)
      • #1702 (issue #17) "Trade history and performance" (backlog, 1pt)
    • No items in in_progress block this spike
    • Dependencies documented in issue Lineage section: "Decision gate for all mobile app work in sprint 4"

    Acceptance Criteria

    Spike uses Deliverables instead of Acceptance Criteria (per template). Assessment:

    • docs/architecture/mobile-framework-adr.md created — verifiable by file existence and ADR content check
    • Follow-up ticket file targets updated — verifiable by checking issue #6 children for revised file paths
    • Team decision recorded with rationale — verifiable by ADR content inspection (somewhat redundant with deliverable 1, but acceptable)

    All deliverables are agent-verifiable.

    Blast Radius

    • Decision affects all 5 sprint 4 frontend items (all currently in backlog)
    • No existing code in the repo (docs only) — no breaking changes possible
    • Note: arch-frontend already documents "Turbo Native iOS shell wrapping Rails views with Capacitor native bridge". This spike may confirm or revise that assumption. No conflict — spikes are intended for exactly this kind of validation.
    • No sibling services or downstream consumers affected

    Decomposition Assessment

    No decomposition needed.

    • File targets: 1 docs file
    • Deliverables: 3 (all part of single investigation)
    • Repos: 1
    • Estimated work: 1 session (within time-box)

    Recommendation

    No action needed.

  • Review: Trade history and performance review-1702-2026-07-02

    Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- references parent #6 and dependency #3
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- well-formed As/I want/So that
    • [x] Context -- explains trade records origin, read-only nature, navigation flow
    • [x] File Targets -- 2 targets (controller + views), clear boundary with DO NOT TOUCH list
    • [x] Feature Flag -- mobile_trade_history specified (note: no docs/feature-flags.md exists yet in repo; template says skip with "none" when absent, but forward planning is acceptable)
    • [x] Acceptance Criteria -- 4 criteria, all testable
    • [x] Test Expectations -- unit + integration tests with run command
    • [x] Constraints -- model ownership boundary, pagination, Turbo Frames
    • [x] Checklist -- standard 3-item checklist
    • [x] Related -- project slug and parent/dependency issues

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder story
    • [x] story note verified -- story-kalshi-assistant-portfolio-builder exists in pal-e-docs
    • [x] story entry verified -- portfolio-builder row found in project-kalshi-assistant user-stories section
    • [x] arch:frontend label -- Frontend architecture component
    • [x] arch note verified -- arch-frontend note exists in pal-e-docs (Turbo Native iOS shell, Hotwire/Turbo views)
    • [x] Forgejo issue -- ldraney/kalshi-assistant#17, state: open

    File Targets

    • [x] app/controllers/trades_controller.rb -- to be created. Standard Rails controller path. Consistent with scaffold pattern from #1.
    • [x] app/views/trades/ -- to be created. Standard Rails view directory. Consistent with Rails conventions.
    • [x] DO NOT TOUCH boundary -- app/models/trade.rb (owned by #3), app/controllers/dashboard_controller.rb (separate ticket). Clean ownership separation.

    Note: The Rails app does not exist yet (greenfield project). File targets cannot be verified against existing code because dependencies #1 (Rails scaffold) and #3 (strategy engine) have not been implemented. Paths follow standard Rails conventions and are reasonable.

    Repo Placement

    OK -- issue filed on ldraney/kalshi-assistant, file targets are Rails files in the same repo. Single-repo ticket, no cross-repo concerns.

    Dependencies

    • #1 (Rails app scaffold) -- implicit dependency, currently in todo column (sprint 1)
    • #3 (Strategy engine -- trade records) -- explicit dependency, currently in todo column (sprint 2). Provides the Trade model this ticket consumes.
    • #16 (Live strategy dashboard) -- explicit dependency ("navigation context comes from the dashboard"), currently in backlog column (sprint 4). Same sprint but dashboard links to trade history.
    • #6 (Mobile app) -- parent issue, currently in backlog (sprint 4).

    Dependencies are documented in the Lineage section. Sprint ordering is correct: sprint 1 and 2 dependencies must complete before this sprint 4 work begins.

    Acceptance Criteria

    4 criteria, all verifiable by an agent:

    • Trade history table sorted by date (newest first) -- verifiable via integration test
    • Filter by date range -- verifiable via controller test
    • Filter by market or outcome -- verifiable via controller test
    • Summary stats (P&L, win rate, total trades) -- verifiable via unit test

    Test run command is valid: bin/rails test test/controllers/trades_controller_test.rb

    Blast Radius

    Minimal. This is a read-only view ticket -- it adds a controller and views that consume data from the Trade model (owned by #3). No data mutations. No downstream consumers. No cross-service impact. The Turbo Frames constraint keeps updates scoped to the trades page.

    Decomposition Assessment

    No decomposition needed.

    • 2 file targets in 1 repo -- under threshold
    • 4 acceptance criteria -- under threshold
    • Estimated agent work: well under 5 minutes (standard CRUD controller + views)
    • 1 point ticket -- correctly sized

    Recommendation

    No action needed. Scope is solid, traceability complete, and ticket is correctly sized for a single agent pass.

    Minor observation: The Feature Flag section specifies mobile_trade_history but the repo has no docs/feature-flags.md. Per template guidance, this should be "none" when no flag infrastructure exists. However, this is forward planning and not blocking -- the flag can be implemented when the flag infrastructure is created as part of the Rails scaffold or a separate ticket.

  • Verdict: READY

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- references parent #6, dependencies on spike, #10, #1
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- As a Kalshi trader / securely enter and manage API credentials
    • [x] Context -- explains Kalshi API key ID + RSA private key requirement, encryption at rest, credential verification flow
    • [x] File Targets -- 3 create targets, 2 do-not-touch targets
    • [x] Feature Flag -- mobile_credential_management, global, disabled by default
    • [x] Acceptance Criteria -- 5 criteria in when/then format
    • [x] Test Expectations -- 4 tests with run command
    • [x] Constraints -- encryption, pattern adherence, error handling, one-set-per-user
    • [x] Checklist -- PR, tests, no unrelated changes
    • [x] Related -- project, parent, dependencies

    Traceability

    • [x] story:credential-onboarding label -- Credential Onboarding
    • [x] story note verified -- found in project-kalshi-assistant user-stories section (key: credential-onboarding, role: Consumer, metric: 90% of users complete setup without support)
    • [x] arch:frontend label -- Frontend component
    • [x] arch note verified -- arch-frontend note exists ("Frontend: kalshi-assistant", architecture, active)
    • [x] arch:auth label -- Authentication component
    • [x] arch note verified -- arch-auth note exists ("Authentication: kalshi-assistant", architecture, active)
    • [x] Forgejo issue -- ldraney/kalshi-assistant#14, open

    File Targets

    • [x] app/controllers/credentials_controller.rb -- TO CREATE: standard Rails CRUD controller. Does not exist yet (expected: depends on Rails scaffold #1).
    • [x] app/views/credentials/ -- TO CREATE: form views for key input, status display, verification. Does not exist yet (expected).
    • [x] app/models/credential.rb -- TO CREATE: model with encrypted attributes. Does not exist yet (expected).
    • [x] Do-not-touch: app/controllers/dashboard_controller.rb -- correctly scoped out (separate ticket)
    • [x] Do-not-touch: app/controllers/strategies_controller.rb -- correctly scoped out (separate ticket)

    Note: No application code exists yet. The repo is docs-only. All file targets are to-be-created, which is correct since this ticket depends on Rails scaffold (#1) being completed first. The Kalshi API docs confirm RSA-PSS key-pair signing, consistent with the ticket's description of API key ID + RSA private key storage.

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant, matches ### Repo field. Single-repo scope.

    Dependencies

    • #1 (Rails app scaffold with Kalshi API client) -- board item #1686, todo column -- DOCUMENTED in Lineage, BLOCKING
    • #10 (Keycloak realm, client, users, and login theme) -- board item #1693, todo column -- DOCUMENTED in Lineage, BLOCKING
    • #13 (Spike: Mobile framework decision) -- board item #1698, backlog column -- DOCUMENTED as "spike (child of #6)" in Lineage, BLOCKING

    All three dependencies are correctly identified in the Lineage section and none are complete yet. This ticket is appropriately in backlog until its dependencies move through the pipeline.

    Acceptance Criteria

    5 criteria, all in clear when/then format and verifiable by an agent:

    1. Form with API key ID and RSA private key fields (PEM paste or file upload) -- verifiable via view rendering
    2. Submit valid credentials -> stored encrypted + success message -- verifiable via model test + integration test
    3. Verify Credentials button -> test API call + status display -- verifiable via controller test with mocked API
    4. Update existing credentials -- verifiable via CRUD integration test
    5. Revoke credentials with confirmation dialog -- verifiable via destroy action test

    Test expectations include 4 tests with a concrete run command. Testable and complete.

    Blast Radius

    Minimal. Greenfield application with no existing code. The Kalshi API docs (docs/api/overview.md, docs/api/openapi.yaml) confirm the RSA-PSS signing approach matches what the ticket specifies. No downstream consumers to affect. No sibling services to check for similar patterns.

    Decomposition Assessment

    • 3 file targets in 1 repo -- OK (under threshold of >3 across >2 repos)
    • 5 acceptance criteria -- OK (not exceeding 5)
    • Estimated agent work: under 5 minutes (standard Rails CRUD + encrypted attributes) -- OK

    No decomposition needed.

    Recommendation

    No action needed. Scope is solid, traceability is complete, template is filled, and the ticket fits comfortably in a single agent pass.

  • Review: Live strategy dashboard review-1701-2026-07-02

    Verdict: READY

    Re-review: Previous review incorrectly flagged arch-frontend and arch-app as missing. Both confirmed to exist. Verdict upgraded from NEEDS_REFINEMENT to READY.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- references parent #6, dependencies #4 and #1
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- As a Kalshi trader, I want a real-time dashboard...
    • [x] Context -- explains dashboard purpose, Turbo Streams approach, traffic expectations
    • [x] File Targets -- 3 create targets, 2 do-not-touch entries
    • [x] Feature Flag -- mobile_live_dashboard, global, disabled by default
    • [x] Acceptance Criteria -- 4 When/Then criteria
    • [x] Test Expectations -- unit + integration tests with run command
    • [x] Constraints -- Turbo Streams, mobile perf, Rails patterns, edge cases
    • [x] Checklist -- standard 3-item checklist
    • [x] Related -- project, parent, and dependency references

    Traceability

    • [x] story:app-experience label -- "App Experience" story
    • [x] story note verified -- found in project-kalshi-assistant user-stories section (key: app-experience, role: Consumer)
    • [x] arch:frontend label -- "Frontend: kalshi-assistant"
    • [x] arch note verified -- arch-frontend (id 2343) exists in pal-e-docs, status active, project kalshi-assistant
    • [x] arch:app label -- "Application Domain: Kalshi Assistant"
    • [x] arch note verified -- arch-app (id 2349) exists in pal-e-docs, status active, project kalshi-assistant
    • [x] Forgejo issue -- ldraney/kalshi-assistant#16, state: open

    File Targets

    • [x] app/controllers/dashboard_controller.rb -- to be created; follows Rails convention; parent directory will exist after #1 (Rails scaffold)
    • [x] app/views/dashboard/ -- to be created; standard Rails view directory
    • [x] app/channels/dashboard_channel.rb -- to be created; Action Cable channel for Turbo Streams
    • [x] Do-not-touch: app/services/watchdog_service.rb (from #4) -- correctly scoped out
    • [x] Do-not-touch: app/controllers/trades_controller.rb (separate ticket) -- correctly scoped out

    Note: No app/ directory exists yet. This is expected -- the Rails scaffold (#1) must complete first. All file targets are new files to create, following standard Rails conventions.

    Repo Placement

    OK. Issue filed in ldraney/kalshi-assistant; all work targets the same repo. Single-repo scope.

    Dependencies

    • #1 (Rails app scaffold) -- in todo column. Hard dependency: provides app/ directory structure and base controller patterns. Must complete before this ticket starts.
    • #4 (Watchdog) -- in todo column. Data dependency: watchdog generates the strategy status data displayed on the dashboard. Must complete before dashboard can show real watchdog data.
    • #6 (Mobile app parent) -- in backlog. This is the parent decomposition issue; not a blocking dependency.
    • Credential UI (child of #6) -- referenced in Lineage; not a direct blocker for dashboard functionality.

    Dependencies are correctly documented in the Lineage section. Sprint ordering (sprint:4) aligns with dependency chain (#1 sprint:1, #4 sprint:3).

    Acceptance Criteria

    4 criteria, all in When/Then format. Each is agent-verifiable:

    • AC1: Active positions with market prices -- verifiable via controller test and integration test
    • AC2: Watchdog trigger status display -- verifiable via integration test with mock data
    • AC3: Real-time updates via Turbo Streams -- verifiable via Action Cable integration test
    • AC4: P&L display (unrealized + realized) -- verifiable via unit test on calculation logic

    Test expectations include specific test file paths and a run command. Complete and testable.

    Blast Radius

    Minimal. Greenfield feature in a repo with no existing application code. No downstream consumers affected. Dashboard reads from services defined in other tickets (#4 watchdog, #2 market scanner) but does not modify them.

    Decomposition Assessment

    No decomposition needed.

    • 3 file targets in 1 repo -- under threshold (max 3 across 2+ repos)
    • 4 acceptance criteria -- under threshold (max 5)
    • 2 story points -- fits single agent pass well under 5-minute estimate

    Recommendation

    No action needed. Scope is solid, traceability is complete (including backing notes for both arch labels and user story), template is fully populated, and the ticket fits in a single agent pass.

  • Verdict: APPROVED

    Re-review of board item #1700. Previous review incorrectly flagged arch-frontend and arch-app as missing. Both architecture notes exist and are verified.

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- references parent #6 and dependencies #3, #1
    • [x] Repo -- ldraney/kalshi-assistant
    • [x] User Story -- present
    • [x] Context -- present, explains relationship to strategy engine and watchdog
    • [x] File Targets -- present (create targets + do-not-touch boundaries)
    • [x] Feature Flag -- mobile_strategy_config, global, disabled by default
    • [x] Acceptance Criteria -- 4 items
    • [x] Test Expectations -- unit + integration with run command
    • [x] Constraints -- present
    • [x] Checklist -- present
    • [x] Related -- present

    Traceability

    • [x] story:portfolio-builder label -- AI Portfolio Builder
    • [x] story note verified -- story-kalshi-assistant-portfolio-builder exists (id 2321, active)
    • [x] story entry verified -- found in project-kalshi-assistant user-stories section
    • [x] arch:frontend label -- Frontend: kalshi-assistant
    • [x] arch note verified -- arch-frontend exists (id 2343, active)
    • [x] arch:app label -- Application Domain: Kalshi Assistant
    • [x] arch note verified -- arch-app exists (id 2349, active)
    • [x] Forgejo issue -- ldraney/kalshi-assistant#15, open

    File Targets

    • [x] app/controllers/strategies_controller.rb -- to create: CRUD for strategy configuration. Repo is greenfield (no Rails structure yet), appropriate since this depends on #1 Rails scaffold (sprint:1)
    • [x] app/views/strategies/ -- to create: strategy selection form, parameter config, budget allocation. Same greenfield note applies
    • [x] app/models/strategy.rb -- correctly marked as do-not-touch (owned by #3)
    • [x] app/services/watchdog_service.rb -- correctly marked as do-not-touch (owned by #4)

    Repo Placement

    OK. Issue filed on ldraney/kalshi-assistant, Repo section specifies ldraney/kalshi-assistant. Single repo, no cross-repo concerns.

    Dependencies

    • #1 (Rails scaffold, sprint:1) -- hard dependency, provides base app structure. Board item 1686, currently in todo.
    • #3 (Strategy engine DB schema, sprint:2) -- hard dependency, defines the Strategy model this UI reads/writes. Board item 1688, currently in todo.
    • #4 (Watchdog, sprint:3) -- soft dependency, this UI must match the watchdog service interface for configuration parameters. Board item 1689, currently in todo.
    • #6 (Mobile app, sprint:4) -- parent issue. Board item 1691, currently in backlog.
    • All dependencies are documented in the issue's Lineage, Context, and Constraints sections. Sprint ordering is correct: 1 -> 2 -> 3 -> 4.

    Acceptance Criteria

    4 criteria, all agent-verifiable. Test expectations include unit and integration tests with specific run command (bin/rails test). Criteria cover CRUD operations and budget validation logic. No missing criteria for the stated scope.

    Blast Radius

    Low. Creates new controller and views only. Explicitly boundaries off the strategy model (#3) and watchdog service (#4). No existing code to conflict with (greenfield repo). Feature flag gates the new UI surface.

    Decomposition Assessment

    No decomposition needed. 2 file targets in 1 repo, 4 acceptance criteria, estimated agent work under 5 minutes. Well within single-pass scope.

    Recommendation

    No action needed.

Architecture 13
  • Kubernetes Deployment: prediction-assistant

    How prediction-assistant is deployed to the k3s cluster. Kustomize overlays define environment-specific configuration, Woodpecker CI builds and pushes images to Harbor, and ArgoCD syncs manifests to the cluster. Tailscale funnel + Traefik ingress handle HTTPS routing.

    Diagram

    graph TB
        subgraph CI["Woodpecker CI"]
            TEST["bin/rails test"]
            BUILD["docker buildx
    build + push"] end subgraph Registry["Harbor Registry"] IMG["harbor.tail5b443a.ts.net
    /ldraney/prediction-assistant
    :SHA / :latest"] end subgraph Repo["prediction-assistant repo"] K8S_BASE["k8s/base/
    deployment, worker, service, configmap"] K8S_PROD["k8s/overlays/prod/
    ingress, cluster-issuer, patches"] K8S_DEV["k8s/overlays/dev/
    resource patches"] K8S_MON["k8s/monitoring/
    service-monitor, rules, grafana"] end subgraph ArgoCD["ArgoCD"] APP["Application CR
    prediction-assistant"] end subgraph Cluster["k3s Cluster — prediction-assistant namespace"] WEB["Deployment: web
    bin/rails server"] WORKER["Deployment: worker
    bin/rails solid_queue:start"] SVC["Service: ClusterIP :80 → :3000"] ING["Traefik Ingress
    prediction-assistant.com"] TLS["cert-manager
    Let's Encrypt TLS"] FUNNEL["Tailscale Funnel
    HTTPS entrypoint"] end TEST -->|pass| BUILD BUILD --> IMG IMG --> APP K8S_PROD --> APP APP -->|sync| WEB APP -->|sync| WORKER APP -->|sync| SVC APP -->|sync| ING ING --> TLS FUNNEL --> ING SVC --> WEB

    Directory Structure

    k8s/
    ├── base/
    │   ├── kustomization.yaml      # namespace: prediction-assistant, shared labels
    │   ├── deployment.yaml          # web pod (Rails server, port 3000)
    │   ├── worker-deployment.yaml   # Solid Queue worker pod
    │   ├── service.yaml             # ClusterIP :80 → :3000
    │   └── configmap.yaml           # RAILS_ENV, log, threads, port
    ├── overlays/
    │   ├── prod/
    │   │   ├── kustomization.yaml   # 2 web replicas, prod resources, log=info
    │   │   ├── ingress.yaml         # Traefik ingress for prediction-assistant.com
    │   │   ├── cluster-issuer.yaml  # Let's Encrypt ACME via cert-manager
    │   │   └── redirect-middleware.yaml  # HTTP → HTTPS redirect
    │   └── dev/
    │       └── kustomization.yaml   # 1 replica each, smaller limits, log=debug
    └── monitoring/
        ├── service-monitor.yaml     # Prometheus scrape config (/metrics, 30s)
        ├── prometheus-rules.yaml    # Golden signal alerts
        └── grafana-dashboard-configmap.yaml
    

    Components

    Component Purpose Notes
    Base Kustomization Shared resources for all environments Sets namespace prediction-assistant, common labels (app.kubernetes.io/name, managed-by)
    Web Deployment Rails application server Image from Harbor, init container runs db:prepare before server start, liveness/readiness on /up
    Worker Deployment Solid Queue background job processor Same image, runs bin/rails solid_queue:start, liveness via pgrep -f solid_queue
    Service ClusterIP routing to web pods Port 80 → targetPort 3000
    ConfigMap Non-secret environment variables RAILS_ENV=production, RAILS_LOG_TO_STDOUT=true, RAILS_MAX_THREADS=3, PORT=3000
    Secrets (external) Sensitive credentials prediction-assistant-secrets: DATABASE_URL, SECRET_KEY_BASE, KALSHI_API_KEY_ID, KALSHI_PRIVATE_KEY (API keys optional)

    Image Build & Push

    Woodpecker CI (.woodpecker.yaml) handles the build pipeline:

    1. Test step: Runs bin/rails test against a Postgres 16 sidecar service.
    2. Build-push step (main branch only): Uses woodpeckerci/plugin-docker-buildx to build a multi-stage Docker image and push to Harbor.

    Tags pushed: ${CI_COMMIT_SHA} and latest. Registry: harbor.tail5b443a.ts.net/ldraney/prediction-assistant.

    The Dockerfile uses a two-stage build: build stage (ruby:3.4.9-slim) installs gems, precompiles bootsnap + assets; runtime stage copies artifacts into a minimal image running as non-root user rails (UID 1000).

    Kustomize Overlays

    Setting Dev Prod
    Web replicas 1 2
    Worker replicas 1 1
    Web CPU request/limit 50m / 250m 200m / 1 core
    Web memory request/limit 128Mi / 256Mi 512Mi / 1Gi
    Worker CPU request/limit 50m / 250m 200m / 1 core
    Worker memory request/limit 128Mi / 256Mi 512Mi / 1Gi
    RAILS_LOG_LEVEL debug info
    Ingress / TLS none Traefik + Let's Encrypt

    ArgoCD Sync Pattern

    ArgoCD watches the repo's k8s/overlays/prod path (registered via the argocd-app Terraform module in pal-e-services). On each merge to main:

    1. Woodpecker CI builds and pushes a new image tagged with the commit SHA + latest.
    2. ArgoCD detects manifest changes and syncs the Kustomize overlay.
    3. Kubernetes pulls the updated image from Harbor using the robot-account pull secret provisioned by the harbor-robot Terraform module.

    Ingress & TLS

    Production ingress chain:

    1. Tailscale Funnel — Public HTTPS entrypoint provisioned by the tailscale-funnel Terraform module. Maps external traffic into the Tailscale network.
    2. Traefik Ingress Controller — Routes prediction-assistant.com to the prediction-assistant-web ClusterIP service.
    3. cert-manager ClusterIssuerletsencrypt-prod issues TLS certificates via HTTP-01 challenge through Traefik.
    4. Redirect Middleware — Traefik middleware forces HTTP → HTTPS with a permanent redirect.

    DNS (managed in pal-e-platform via the godaddy-dns Terraform module) points prediction-assistant.com to the Tailscale funnel address.

    Monitoring

    The k8s/monitoring/ directory defines observability resources:

    • ServiceMonitor: Prometheus scrapes /metrics every 30s from pods in the prediction-assistant namespace.
    • PrometheusRule: Golden-signal alerts — HTTP error rate > 5%, p95 latency > 2s, pod not ready for 5m, Solid Queue failures or backlog > 100 jobs.
    • Grafana dashboard: ConfigMap-provisioned dashboard for at-a-glance service health.

    Key Decisions

    • Kustomize over Helm: The app's configuration is simple enough that Kustomize overlays (base + patches) keep things readable without Helm templating complexity.
    • In-repo manifests: k8s manifests live alongside application code in the k8s/ directory. Changes to infrastructure and code ship together in the same PR.
    • Init container for migrations: The web deployment runs db:prepare in an init container, ensuring schema is ready before the server starts. Only the web pod runs migrations; the worker relies on web readiness.
    • Separate web and worker deployments: Allows independent scaling and resource allocation. The worker runs Solid Queue for background jobs (market scanning, order execution).
    • Harbor robot accounts: Pull secrets are provisioned by Terraform (pal-e-services), not manually created. Scoped to the prediction-assistant Harbor project.
    • Tailscale funnel over NodePort/LoadBalancer: No cloud load balancer needed on bare-metal k3s. Tailscale funnel provides a stable public entrypoint with automatic TLS.
    • Non-root container: Runtime image runs as UID 1000 (rails user) for security best practice.
    • Docker buildx over kaniko: Woodpecker CI uses plugin-docker-buildx for multi-stage builds. Tags both :SHA and :latest.
  • Kustomize Overlays: kalshi-assistant

    Directory structure and overlay strategy for kalshi-assistant manifests in pal-e-deployments. Follows the pattern established by landscaping-assistant. Two overlays: prod (full cluster deployment) and dev (nginx proxy to developer workstation).

    Diagram

    graph TB
        subgraph PalEDeployments["pal-e-deployments repo"]
            subgraph KalshiDir["kalshi-assistant/"]
                subgraph Base["base/"]
                    B_KUST["kustomization.yaml"]
                    B_DEPLOY["deployment.yaml"]
                    B_SVC["service.yaml"]
                    B_CM["configmap.yaml"]
                end
    
                subgraph Prod["overlays/prod/"]
                    P_KUST["kustomization.yaml"]
                    P_NS["namespace.yaml"]
                    P_ING["ingress.yaml"]
                    P_SM["servicemonitor.yaml"]
                    P_RENAME["rename-patches.yaml"]
                end
    
                subgraph Dev["overlays/dev/"]
                    D_KUST["kustomization.yaml"]
                    D_NGINX["nginx-proxy-conf"]
                    D_DEPLOY["deployment-override.yaml
    (nginx image)"] D_CM["configmap.yaml
    (dev machine IP)"] end end end subgraph Targets["Deployment Targets"] PROD_K8S["Prod: k3s cluster
    real app pods"] DEV_K8S["Dev: k3s cluster
    nginx proxy to dev machine"] end Base --> Prod Base --> Dev Prod --> PROD_K8S Dev --> DEV_K8S

    Components

    Component Purpose Notes
    base/deployment.yaml Core deployment spec shared by all overlays Container image, ports, resource limits, health probes
    base/service.yaml ClusterIP service exposing the app port Referenced by ingress (prod) and nginx proxy (dev)
    base/configmap.yaml Shared configuration values Environment-specific values patched by overlays
    overlays/prod/namespace.yaml Namespace resource for prod Created by IaC but declared here for completeness
    overlays/prod/ingress.yaml Ingress resource for Tailscale funnel routing Routes external traffic to the service
    overlays/prod/servicemonitor.yaml Prometheus ServiceMonitor for metrics scraping Targets the /metrics endpoint; see arch-observability
    overlays/prod/rename-patches.yaml Strategic merge patches for prod naming Adds namespace prefix, labels, annotations for prod context
    overlays/dev/nginx-proxy-conf Nginx config that proxies to dev machine IP Enables cluster-hosted dev URL pointing to localhost dev server
    overlays/dev/deployment-override.yaml Replaces app container with nginx Nginx image serves as reverse proxy to dev workstation
    overlays/dev/configmap.yaml Dev machine IP and port Updated when developer's Tailscale IP changes

    Key Decisions

    • Landscaping-assistant as reference pattern: kalshi-assistant follows the exact directory structure and overlay strategy proven by landscaping-assistant. Consistency across services reduces cognitive load.
    • Dev overlay uses nginx proxy, not port-forward: Instead of running the app in-cluster during development, the dev overlay deploys an nginx reverse proxy that forwards traffic to the developer's local machine. This gives a real cluster URL while the developer iterates locally.
    • Prod overlay includes ServiceMonitor: Observability is wired in at the deployment level, not bolted on later. Every prod deployment includes its ServiceMonitor for Prometheus scraping.
    • Rename patches for namespace scoping: Strategic merge patches handle namespace-specific naming so the base manifests remain environment-agnostic.
    • No Helm: Plain Kustomize overlays with YAML patches. The complexity budget is spent on getting the overlay structure right, not on a templating engine.
  • Observability: kalshi-assistant

    Monitoring, alerting, and dashboards for kalshi-assistant. All observability resources are configured in pal-e-platform under terraform/modules/monitoring/main.tf, following the patterns established by existing services.

    Diagram

    graph TB
        subgraph KalshiNS["kalshi-assistant namespace"]
            APP["kalshi-assistant pod
    /metrics endpoint"] SM["ServiceMonitor
    (from Kustomize prod overlay)"] end subgraph Monitoring["monitoring namespace"] PROM["Prometheus"] AM["AlertManager"] RULES["PrometheusRules
    (golden signals)"] BB["blackbox-exporter"] GRAF["Grafana"] end subgraph PalEPlatform["pal-e-platform repo"] TF_MON["terraform/modules/
    monitoring/main.tf"] TF_RULES["alert rules
    (per-service block)"] TF_BB["blackbox targets
    (probe list)"] TF_DASH["dashboard JSON
    (golden signals)"] end subgraph Alerts["Alert Destinations"] EMAIL["Email notifications"] WEBHOOK["Webhook receivers"] end SM -->|scrape config| PROM PROM -->|evaluates| RULES RULES -->|fires| AM AM --> EMAIL AM --> WEBHOOK BB -->|HTTP probes| APP PROM -->|data source| GRAF TF_MON --> RULES TF_MON --> TF_BB TF_MON --> TF_DASH TF_RULES -.->|defines| RULES TF_BB -.->|defines| BB TF_DASH -.->|provisions| GRAF

    Components

    Component Purpose Notes
    ServiceMonitor Tells Prometheus to scrape kalshi-assistant's /metrics endpoint Deployed via Kustomize prod overlay; matches service labels
    PrometheusRules Alert rules for golden signals (latency, traffic, errors, saturation) Defined in pal-e-platform monitoring module; follows existing service patterns
    AlertManager Routes and deduplicates alerts Shared cluster-wide; per-service routing via labels
    blackbox-exporter External HTTP probes for uptime monitoring Probes the public Tailscale funnel URL; alerts on non-2xx responses
    Grafana dashboards Golden signals dashboard for kalshi-assistant Request rate, error rate, latency percentiles, resource utilization
    monitoring/main.tf Terraform that provisions all monitoring resources Adding kalshi-assistant means adding a service block following the existing pattern

    Key Decisions

    • Golden signals framework: Every service dashboard tracks the four golden signals (latency, traffic, errors, saturation). This standardization means any team member can read any service dashboard without learning a new layout.
    • Monitoring as code in pal-e-platform: Alert rules, blackbox targets, and dashboards are Terraform resources, not manual Grafana/Prometheus config. Changes are reviewed in PRs and applied deterministically.
    • Blackbox probes for external perspective: In addition to internal metrics scraping, blackbox-exporter probes the public URL. This catches issues that internal metrics miss (DNS, Tailscale funnel, TLS).
    • Follow existing service patterns: kalshi-assistant's monitoring block mirrors the structure used by other services in main.tf. Copy-paste-adapt, not reinvent.
    • ServiceMonitor in Kustomize, rules in Terraform: The scrape target (ServiceMonitor) lives with the deployment manifests. The alert rules and dashboards live in the platform repo. This split matches ownership: app team owns what to expose, platform owns how to alert on it.
  • Infrastructure as Code: kalshi-assistant

    How infrastructure is provisioned for the kalshi-assistant service. OpenTofu (Terraform) modules in pal-e-services onboard each new service, while pal-e-platform manages shared platform resources like DNS.

    Diagram

    graph TB
        subgraph Developer["Developer Workstation"]
            TF["tofu apply
    (pal-e-services)"] TF_PLAT["tofu apply
    (pal-e-platform)"] end subgraph PalEServices["pal-e-services repo"] TFVARS["k3s.tfvars
    service definitions"] MOD_NS["module: namespace"] MOD_HARBOR["module: harbor-robot"] MOD_ARGO["module: argocd-app"] MOD_TS["module: tailscale-funnel"] end subgraph PalEPlatform["pal-e-platform repo"] DNS_MOD["module: godaddy-dns"] GODADDY["GoDaddy API"] end subgraph K3s["k3s Cluster"] NS["kalshi-assistant
    namespace"] ROBOT["Harbor robot account
    + pull secret"] ARGOAPP["ArgoCD Application
    kalshi-assistant"] FUNNEL["Tailscale Funnel
    HTTPS ingress"] end TF --> TFVARS TFVARS --> MOD_NS --> NS TFVARS --> MOD_HARBOR --> ROBOT TFVARS --> MOD_ARGO --> ARGOAPP TFVARS --> MOD_TS --> FUNNEL TF_PLAT --> DNS_MOD --> GODADDY

    Components

    Component Purpose Notes
    k3s.tfvars Declares kalshi-assistant as a service entry Single source of truth for namespace name, Harbor project, ArgoCD app name, funnel hostname
    namespace module Creates k8s namespace Idempotent; includes resource quotas and labels
    harbor-robot module Creates Harbor robot account and k8s pull secret Scoped to the service's Harbor project; secret placed in namespace
    argocd-app module Registers ArgoCD Application CR Points at pal-e-deployments repo path for this service
    tailscale-funnel module Provisions Tailscale funnel for HTTPS ingress Maps public hostname to internal k8s service
    godaddy-dns module Manages DNS records via GoDaddy API Lives in pal-e-platform; CNAME or A records pointing to Tailscale funnel

    Key Decisions

    • OpenTofu over raw kubectl: Declarative state management prevents drift and enables reproducible onboarding. Every new service follows the same module pattern.
    • Split repos (services vs platform): pal-e-services handles per-service resources; pal-e-platform handles shared infra (DNS, monitoring). Separation of concerns keeps blast radius small.
    • tfvars-driven onboarding: Adding a new service means adding an entry to k3s.tfvars rather than writing new Terraform. This keeps onboarding fast and consistent.
    • GoDaddy DNS via Terraform: DNS is managed as code alongside infrastructure, not manually in a web console. Changes are auditable in git.
  • API Integration

    Kalshi REST API integration layer. Answers: how does the app communicate with the Kalshi exchange?

    Diagram

    sequenceDiagram
        participant App as kalshi-assistant
        participant Auth as RSA-PSS Signer
        participant API as Kalshi API /trade-api/v2
        participant WS as Kalshi WebSocket
    
        Note over App,WS: Authenticated Request Flow
        App->>Auth: Sign {timestamp}{METHOD}{path}
        Auth->>App: RSA-PSS SHA-256 signature
        App->>API: Request + KALSHI-ACCESS-KEY + KALSHI-ACCESS-TIMESTAMP + KALSHI-ACCESS-SIGNATURE
        API->>App: JSON response
    
        Note over App,WS: Market Scanning (Read Path)
        App->>API: GET /markets?series_ticker={series}
        API->>App: Market list with yes_bid, no_bid
        App->>API: GET /markets/{ticker}/orderbook
        API->>App: Orderbook depth
    
        Note over App,WS: Order Execution (Write Path)
        App->>API: POST /portfolio/orders
        API->>App: Order confirmation
        App->>API: GET /portfolio/positions
        API->>App: Current positions
    
        Note over App,WS: Real-time Monitoring
        App->>WS: Connect + authenticate
        WS->>App: Market updates (streaming)
    

    Components

    Component Purpose Notes
    RSA-PSS Signer Signs each API request with user's private key RSA-PSS SHA-256. Signs {timestamp_ms}{HTTP_METHOD}{path}
    Kalshi REST API Exchange API for markets, orders, portfolio Base: https://external-api.kalshi.com/trade-api/v2
    Kalshi WebSocket Real-time market data streaming wss://api.kalshi.com/trade-api/ws/v2
    Demo API Sandbox environment for development https://external-api.demo.kalshi.co/trade-api/v2

    Key Decisions

    • RSA-PSS over session tokens — Kalshi requires per-request RSA-PSS SHA-256 signing. No OAuth, no session cookies. Each request carries three headers: KALSHI-ACCESS-KEY, KALSHI-ACCESS-TIMESTAMP, KALSHI-ACCESS-SIGNATURE.
    • Basic tier rate limits constrain scanning — 20 reads/sec and 10 writes/sec at Basic tier. Watchdog strategy (scanning all live markets) must stay within these bounds. Poll only same-day markets, batch where possible.
    • Demo-first development — All development and testing against demo.kalshi.co. Production keys used only in deployed app with real user accounts.
    • No fund handling — The app never touches deposits or withdrawals. All money flows through Kalshi directly. We only read balances and execute trades via API.
    • WebSocket for live monitoring — REST polling is sufficient for market scanning at Basic tier, but WebSocket provides real-time updates for active position monitoring without burning read quota.

    Rate Limits

    Tier Read/sec Write/sec
    Basic 20 10
    Advanced 30 30
    Premier 100 100
    Prime 400 400

    Key Endpoint Groups

    Group Key Endpoints Usage
    Markets GET /markets, GET /markets/{ticker}/orderbook Market scanning, orderbook analysis
    Orders POST /portfolio/orders, DELETE /portfolio/orders/{id} Trade execution and cancellation
    Portfolio GET /portfolio/positions, GET /portfolio/balance Position tracking, balance checks
    Events GET /events, GET /events/{ticker} Event metadata, fee schedules
  • Frontend Architecture

    Turbo Native iOS shell wrapping Rails views with Capacitor native bridge. Answers: how does the user interact with the app?

    Diagram

    graph TB
        subgraph iOS["iOS App (Turbo Native)"]
            TN[Turbo Native Shell]
            CAP[Capacitor Bridge]
            PUSH[Push Notifications]
        end
    
        subgraph Rails["Rails Server"]
            VIEWS["Rails Views (Hotwire/Turbo)"]
            API_C[API Controllers]
            TF[Turbo Frames]
            TS[Turbo Streams]
        end
    
        subgraph Dashboard["Strategy Dashboard"]
            POS[Live Positions]
            HIST[Trade History]
            WATCH[Watchdog Triggers]
            PERF[Performance Metrics]
        end
    
        TN -->|"loads HTML"| VIEWS
        CAP -->|"native APIs"| TN
        PUSH -->|"alerts"| TN
        VIEWS --> TF
        VIEWS --> TS
        TF --> Dashboard
        TS -->|"real-time updates"| POS
        API_C -->|"JSON"| CAP
    

    Components

    Component Purpose Notes
    Turbo Native Shell iOS wrapper that renders Rails HTML views natively No React Native or Flutter. Server-rendered HTML with native navigation chrome.
    Capacitor Bridge Native API access from web views Biometric auth, push notifications, secure keychain storage for API keys
    Rails Views (Hotwire) Server-rendered UI with Turbo Frames and Streams Strategy dashboard, position views, trade history
    Turbo Frames Partial page updates without full reload Dashboard sections update independently
    Turbo Streams Real-time DOM updates via WebSocket Live position values, watchdog trigger alerts
    Strategy Dashboard Main user interface showing portfolio state Live positions, trade history, active watchdog triggers, performance metrics

    Key Decisions

    • Turbo Native over React Native — Server-rendered HTML wrapped in a native shell. One codebase (Rails views) serves both web and mobile. Faster iteration, no JavaScript framework churn.
    • Capacitor for native bridge — Provides access to iOS APIs (keychain, biometrics, push) without leaving the Turbo Native pattern. Lighter than Cordova.
    • Hotwire for interactivity — Turbo Frames for partial updates, Turbo Streams for real-time pushes. No SPA complexity. Dashboard sections refresh independently.
    • Strategy dashboard as primary view — Users see live positions, trade history, and active watchdog triggers in one view. Performance metrics show cumulative P&L and win rate.
    • Kalshi app handles fund management — Our app never shows deposit/withdrawal UI. Users manage funds directly in the Kalshi app. We show read-only balance for context.
  • PostgreSQL Architecture: Kalshi Assistant

    Database schema and storage architecture. Answers: what are the tables and how does the strategy engine use them?

    Diagram

    erDiagram
        users ||--o{ credentials : "has"
        users ||--o{ portfolios : "owns"
        users ||--o{ strategies : "defines"
        strategies ||--o{ portfolios : "drives"
        strategies ||--o| watchdog_configs : "configures"
        strategies ||--o| sizing_configs : "configures"
        portfolios ||--o{ trades : "contains"
        markets ||--o{ trades : "referenced by"
        market_scans ||--o{ markets : "discovers"
    
        users {
            bigint id PK
            string keycloak_sub
            string email
            timestamps created_updated
        }
    
        credentials {
            bigint id PK
            bigint user_id FK
            string kalshi_api_key_id
            text encrypted_private_key
            boolean validated
            datetime last_verified_at
        }
    
        strategies {
            bigint id PK
            bigint user_id FK
            string name
            string strategy_type
            jsonb parameters
            boolean active
        }
    
        watchdog_configs {
            bigint id PK
            bigint strategy_id FK
            decimal buy_threshold
            decimal take_profit_threshold
            decimal cut_loss_threshold
            text series_tickers
        }
    
        sizing_configs {
            bigint id PK
            bigint strategy_id FK
            decimal yes_pct
            decimal no_pct
            integer max_picks
        }
    
        portfolios {
            bigint id PK
            bigint user_id FK
            bigint strategy_id FK
            string name
            decimal budget
            string status
            date trading_date
        }
    
        trades {
            bigint id PK
            bigint portfolio_id FK
            bigint market_id FK
            string kalshi_order_id
            string ticker
            string side
            decimal price
            integer quantity
            string status
            decimal pnl
        }
    
        markets {
            bigint id PK
            string ticker
            string series_ticker
            string title
            decimal yes_bid
            decimal yes_ask
            string status
            datetime close_time
        }
    
        market_scans {
            bigint id PK
            datetime scanned_at
            integer markets_found
            integer above_threshold
        }
    

    Components

    Component Purpose Notes
    users Keycloak-authenticated user reference Maps keycloak_sub to local user; no password stored locally
    credentials Encrypted Kalshi API credentials per user RSA private key encrypted at rest via Rails credentials
    strategies Strategy definitions (watchdog, stacking) Polymorphic via strategy_type + jsonb parameters; avoids STI
    watchdog_configs Threshold parameters for watchdog auto-buy buy_threshold=0.85, take_profit=0.95, cut_loss=0.80
    sizing_configs YES/NO allocation for Option D sizing Default: yes_pct=83, no_pct=17; per-strategy override
    portfolios Daily trading session container Scoped to trading_date; one active per day per strategy
    trades Individual order records with Kalshi reconciliation Tracks kalshi_order_id; pnl computed on settlement
    markets Cached Kalshi market data TTL-based refresh; indexed on ticker and series_ticker
    market_scans Audit log of polling cycles Tracks markets_found and above_threshold per scan

    Key Decisions

    • CNPG shared cluster with 4 databases — primary (app data), cache (Solid Cache), queue (Solid Queue), cable (Solid Cable); follows Rails 8 Solid stack convention on shared CloudNativePG cluster
    • Strategy polymorphism via strategy_type + jsonb — avoids STI complexity; keeps schema simple while supporting future strategy types beyond watchdog and stacking
    • Market data cached in PostgreSQL not Redis — simpler ops; strategy engine needs SQL joins between markets and thresholds for filtering
    • Credentials encrypted at rest — RSA private keys are highly sensitive; validated on save via test API call to Kalshi demo environment
    • Portfolio scoped to trading_date — enforces same-day-only rule at database level; prevents stale multi-day positions from accumulating
    • Decimal types for all monetary and price columns — avoids floating-point rounding errors in trade PnL calculations and Option D sizing math
  • Application Domain: Kalshi Assistant

    Core service architecture and domain logic. Answers: what are the services and how do they interact?

    Diagram

    graph TB
        subgraph Services["Core Services"]
            KC[KalshiClient]
            MS[MarketScanner]
            WD[Watchdog]
            SE[SizingEngine]
        end
    
        subgraph Jobs["Solid Queue Jobs"]
            MSJ[MarketScanJob]
            PMJ[PositionMonitorJob]
        end
    
        subgraph External["External"]
            API[Kalshi API v2]
        end
    
        subgraph Data["Data Layer"]
            DB[(PostgreSQL)]
        end
    
        MSJ -->|"every N seconds"| MS
        MS -->|"uses"| KC
        KC -->|"GET /markets"| API
        MS -->|"caches markets"| DB
        MS -->|"above 85%"| WD
        WD -->|"calculates allocation"| SE
        SE -->|"83/17 YES/NO split"| WD
        WD -->|"places order"| KC
        KC -->|"POST /orders + RSA-PSS"| API
        KC -->|"records trade"| DB
        PMJ -->|"checks positions"| KC
        KC -->|"GET /positions"| API
        PMJ -->|"sell trigger hit"| WD
    

    Components

    Component Purpose Notes
    KalshiClient HTTP wrapper for Kalshi API v2 with RSA-PSS authentication Signs each request with timestamp + method + path; handles rate limiting (20 reads/sec, 10 writes/sec basic tier)
    MarketScanner Polls same-day markets and filters by confidence threshold GET /markets?series_ticker=SERIES&status=open; caches results in markets table; identifies candidates above buy_threshold
    Watchdog Auto-buy when market crosses threshold; auto-sell on triggers Entry at 85%+, take profit at 95%+, cut loss below 80%; creates Trade records in active Portfolio
    SizingEngine Calculates optimal YES/NO allocation per Option D model Default: 83% YES / 17% NO; brute-force optimized to minimize worst-case loss (-$5.77 on $100 budget)
    MarketScanJob Recurring Solid Queue job for market polling Runs every N seconds; calls MarketScanner.scan; creates MarketScan audit record
    PositionMonitorJob Monitors open positions for sell triggers Checks price movement on open trades; triggers Watchdog sell logic on threshold breach

    Key Decisions

    • Service object pattern — each service has a single responsibility: KalshiClient (auth/HTTP), MarketScanner (polling/filtering), Watchdog (buy/sell logic), SizingEngine (allocation math)
    • Watchdog auto-buy at 85%+ — threshold configurable per strategy via watchdog_configs; avoids pregame prediction entirely; rides mid-game momentum when the outcome becomes clear
    • Option D 83/17 split as default sizing — brute-force optimized worst-case loss; stored in sizing_configs for per-strategy override; adjusts based on pick count and confidence levels
    • Rate limit awareness in KalshiClient — tracks request count per second; backs off before hitting basic tier limits (20 reads/sec, 10 writes/sec)
    • MarketScan audit trail — every polling cycle logged with markets_found and above_threshold counts; enables analysis of scan frequency vs. opportunity detection rate
    • Sell triggers as configurable thresholds — take_profit (95%+) and cut_loss (below 80%) stored in watchdog_configs; not hardcoded, tunable per strategy
  • Authentication Architecture

    Two-layer authentication: Keycloak SSO for app access, Kalshi API keys for trading. Answers: how are users authenticated and authorized?

    Diagram

    sequenceDiagram
        participant User
        participant App as iOS App
        participant KC as Keycloak
        participant Rails as Rails API
        participant KS as Kalshi API
    
        Note over User,KS: Layer 1 - App Login (Keycloak SSO)
        User->>App: Open app
        App->>KC: Direct grant (username + password)
        KC->>App: JWT access token + refresh token
        App->>Rails: API requests + Bearer JWT
        Rails->>KC: Validate JWT (JWKS)
        KC->>Rails: Token valid
        Rails->>App: Authorized response
    
        Note over User,KS: Layer 2 - Kalshi API Credentials (One-time Setup)
        User->>App: Enter Kalshi API key ID
        User->>App: Paste RSA private key PEM
        App->>Rails: POST /api/credentials (encrypted)
        Rails->>Rails: Encrypt + store per-user
    
        Note over User,KS: Trading Flow (Both Layers)
        App->>Rails: Execute trade + Bearer JWT
        Rails->>KC: Validate JWT
        Rails->>Rails: Decrypt user Kalshi credentials
        Rails->>KS: Signed API request (RSA-PSS)
        KS->>Rails: Trade result
        Rails->>App: Trade confirmation
    

    Components

    Component Purpose Notes
    Keycloak SSO App-level authentication and user management Direct grant flow (no browser redirect). Realm: kalshi-assistant
    JWT Tokens Stateless auth between iOS app and Rails API Access token + refresh token. Validated via JWKS endpoint.
    Kalshi API Key ID Identifies the user's Kalshi account for API access Public identifier, stored per-user in DB
    RSA Private Key Signs Kalshi API requests on behalf of the user PEM format. Encrypted at rest. Never transmitted after initial setup.
    Credential Store Per-user encrypted storage of Kalshi API credentials Rails encrypted credentials or DB-level encryption

    Key Decisions

    • Two auth layers, not one — Keycloak handles "who is this user of our app?" Kalshi API keys handle "can this user trade?" Separating these means users can log in and browse without having Kalshi credentials set up yet.
    • Direct grant over redirect flow — Mobile apps use Keycloak's direct grant (Resource Owner Password Credentials). No browser redirect needed. Cleaner native UX.
    • Per-user Kalshi credential storage — Each user brings their own Kalshi API key and RSA private key. Keys are encrypted at rest. The app signs requests on behalf of users but never holds their funds.
    • User can revoke access anytime — Users can delete their API key from Kalshi's settings at any time, immediately revoking our trading access. No lock-in.
    • RSA private key never leaves the server — After initial submission, the private key is encrypted and stored server-side. It is used only for signing Kalshi API requests. Never returned to the client.
  • Domain Model: Prediction Assistant arch-domain-prediction-assistant

    Domain Model: Kalshi Assistant

    Diagram

    erDiagram
        User ||--o{ Credential : "has"
        User ||--o{ Portfolio : "owns"
        Credential {
            string kalshi_api_key_id
            text encrypted_private_key
            boolean validated
            datetime last_verified_at
        }
        Portfolio ||--o{ Trade : "contains"
        Portfolio {
            string name
            decimal budget
            string status
            date trading_date
        }
        Strategy ||--o{ Portfolio : "drives"
        Strategy {
            string name
            string strategy_type
            jsonb parameters
            boolean active
        }
        WatchdogConfig ||--|| Strategy : "configures"
        WatchdogConfig {
            decimal buy_threshold
            decimal take_profit_threshold
            decimal cut_loss_threshold
            string series_tickers
        }
        SizingConfig ||--|| Strategy : "configures"
        SizingConfig {
            decimal yes_pct
            decimal no_pct
            integer max_picks
        }
        Trade {
            string kalshi_order_id
            string ticker
            string side
            decimal price
            integer quantity
            string status
            decimal pnl
        }
        Market {
            string ticker
            string series_ticker
            string title
            decimal yes_bid
            decimal yes_ask
            string status
            datetime close_time
        }
        MarketScan ||--o{ Market : "discovers"
        MarketScan {
            datetime scanned_at
            integer markets_found
            integer above_threshold
        }
    

    Components

    Component Purpose Notes
    User App user authenticated via Keycloak Keycloak JWT — maps to CrewMember-style model
    Credential Stores Kalshi API key ID and encrypted RSA private key Encrypted at rest, validated on save via test API call
    Strategy Polymorphic strategy definition (watchdog, stacking) strategy_type + jsonb parameters for flexibility
    WatchdogConfig Threshold configuration for watchdog strategy Buy at 85%, take profit at 95%, cut loss at 80%
    SizingConfig YES/NO allocation ratios for Option D sizing Default: 83% YES / 17% NO
    Portfolio A set of trades for a single trading day Same-day-only constraint enforced at model level
    Trade Individual buy/sell order placed on Kalshi Tracks kalshi_order_id for reconciliation
    Market Cached Kalshi market data Refreshed by MarketScan jobs, TTL-based cache
    MarketScan Audit log of each market polling cycle Tracks markets found and above-threshold count

    Key Decisions

    • Credentials encrypted at rest — RSA private keys are highly sensitive
    • Strategy is polymorphic via strategy_type + jsonb — avoids STI complexity while keeping extensibility
    • Market data cached locally to reduce API calls within rate limits
    • Trade tracks Kalshi order ID for reconciliation
    • Portfolio scoped to trading_date — enforces same-day-only rule at data model level
    • arch-dataflow-kalshi-assistant — how data moves between these entities at runtime
    • arch-deployment-kalshi-assistant — where these entities are hosted
    • project-kalshi-assistant — parent project page
    • convention-architecture-ids — arch: label naming
  • Data Flow: Prediction Assistant arch-dataflow-prediction-assistant

    Data Flow: Kalshi Assistant

    Diagram

    Flow 1: Watchdog Scan

    sequenceDiagram
        participant SQ as Solid Queue
        participant Rails as Rails App
        participant DB as PostgreSQL
        participant Kalshi as Kalshi API
    
        SQ->>Rails: MarketScanJob.perform
        Rails->>Kalshi: GET /markets?series_ticker=SERIES
        Kalshi-->>Rails: Market list with prices
        Rails->>DB: Cache market data
        Rails->>Rails: Check thresholds (yes_bid >= 85%)
        alt Market crosses buy threshold
            Rails->>Kalshi: POST /portfolio/events/orders
            Kalshi-->>Rails: Order confirmation
            Rails->>DB: Create Trade record
        end
    

    Flow 2: Position Monitor

    sequenceDiagram
        participant SQ as Solid Queue
        participant Rails as Rails App
        participant DB as PostgreSQL
        participant Kalshi as Kalshi API
    
        SQ->>Rails: PositionMonitorJob.perform
        Rails->>DB: Load open Trade records
        Rails->>Kalshi: GET /markets/ticker for each position
        Kalshi-->>Rails: Current prices
        Rails->>Rails: Check sell triggers
        alt Sell trigger hit
            Rails->>Kalshi: POST /portfolio/events/orders (sell)
            Kalshi-->>Rails: Sell confirmation
            Rails->>DB: Update Trade with pnl
        end
    

    Flow 3: User Auth

    sequenceDiagram
        participant App as iOS App
        participant KC as Keycloak
        participant Rails as Rails App
    
        App->>KC: Auth Code + PKCE login
        KC-->>App: ID token + access token
        App->>Rails: API requests with Bearer token
        Rails->>KC: Verify token (JWKS)
        Rails-->>App: Strategy dashboard data
    

    Components

    Component Purpose Notes
    Solid Queue Background job runner for scan and monitor loops Rails 8 built-in, no Redis dependency
    Rails App Core application logic, API layer, strategy engine Port 3000, Rails 8
    PostgreSQL Primary data store for all entities Shared CNPG cluster, 4 databases
    Kalshi API External market data and trade execution RSA-PSS SHA-256 auth, rate-limited
    Keycloak Identity provider, SSO Auth Code + PKCE, same pattern as landscaping-assistant
    iOS App Turbo Native shell for mobile access ASWebAuthenticationSession for login

    Key Decisions

    • Solid Queue over Sidekiq — eliminates Redis dependency, sufficient for polling frequency
    • RSA-PSS signature computed per request — Kalshi requires timestamp + method + path signed with private key
    • Market data cached in PostgreSQL not Redis — simpler ops, query-friendly for strategy evaluation
    • Auth Code + PKCE flow — matches landscaping-assistant pattern, works with iOS
    • arch-domain-kalshi-assistant — entity definitions referenced in flows
    • arch-deployment-kalshi-assistant — where these services run
    • project-kalshi-assistant — parent project page
  • Keycloak: prediction-assistant arch-keycloak-prediction-assistant

    Keycloak Configuration

    Keycloak realm and client setup for kalshi-assistant. Follows pal-e-platform and pal-e-services patterns. Answers: how is the identity provider configured?

    Diagram

    graph TB
        subgraph Keycloak["Keycloak Server"]
            REALM["Realm: kalshi-assistant"]
            CLIENT["Client: kalshi-assistant-app"]
            THEME[Custom Login Theme]
            USERS[User Federation]
        end
    
        subgraph Platform["pal-e-platform"]
            THEMES_DIR["keycloak/themes/kalshi-assistant/"]
            LOGIN_THEME["login/theme.properties + templates"]
        end
    
        subgraph Services["pal-e-services"]
            TFVARS["k3s.tfvars realm + client config"]
            TF[Terraform Keycloak Provider]
        end
    
        subgraph App["kalshi-assistant"]
            IOS[iOS App]
            RAILS[Rails API]
        end
    
        TFVARS -->|"provisions"| TF
        TF -->|"creates"| REALM
        TF -->|"creates"| CLIENT
        THEMES_DIR -->|"deployed to"| THEME
        LOGIN_THEME --> THEMES_DIR
        IOS -->|"direct grant"| CLIENT
        RAILS -->|"validate JWT"| REALM
        USERS -->|"manages"| REALM
    

    Components

    Component Purpose Notes
    Realm: kalshi-assistant Identity boundary for the app's users Separate realm per app. Follows pal-e-platform convention.
    Client: kalshi-assistant-app OAuth2 client for the iOS app Direct grant enabled. Public client (no client secret for mobile).
    Custom Login Theme Branded login page for kalshi-assistant Located at keycloak/themes/kalshi-assistant/ in pal-e-platform
    Terraform Keycloak Provider IaC provisioning of realm and client Configured in pal-e-services k3s.tfvars
    pal-e-platform themes dir Theme source files deployed to Keycloak keycloak/themes/{app-name}/login/
    pal-e-services tfvars Realm and client configuration as code k3s.tfvars defines realm name, client ID, grant types

    Key Decisions

    • Follows landscaping-assistant pattern — Realm and client setup mirrors the existing landscaping-assistant Keycloak configuration in pal-e-platform and pal-e-services. Proven pattern, no new infrastructure decisions.
    • Theme at keycloak/themes/{app-name}/ — Custom login page branded for kalshi-assistant. Theme files live in pal-e-platform repo, deployed as part of the Keycloak container image.
    • Terraform-managed configuration — Realm and client provisioned via Terraform Keycloak provider in pal-e-services. Configuration lives in k3s.tfvars alongside other app realms. No manual Keycloak admin console changes.
    • Direct grant for mobile — Public client with Resource Owner Password Credentials grant type. No browser redirect. The iOS app collects username/password and exchanges directly for tokens.
    • Separate realm per app — kalshi-assistant gets its own realm, not a shared one. Users are app-specific. Follows the one-realm-per-app convention across pal-e projects.
  • Deployment: Prediction Assistant arch-deployment-prediction-assistant

    Deployment: Kalshi Assistant

    Diagram

    graph TB
        subgraph Internet
            Browser[Browser / iOS App]
            Domain[prediction-assistant.com]
        end
    
        subgraph Hetzner["Hetzner Edge VPS"]
            Caddy[Caddy Reverse Proxy]
        end
    
        subgraph GoDaddy
            DNS["A Record to Hetzner IP"]
        end
    
        subgraph k3s["k3s Cluster"]
            subgraph ns_prod["kalshi-assistant namespace"]
                Rails[Rails 8 + Solid Queue]
            end
            subgraph ns_dev["kalshi-dev namespace"]
                DevProxy["nginx dev proxy"]
            end
            subgraph ns_pg["postgres namespace"]
                CNPG[PostgreSQL 17 CNPG]
            end
            subgraph ns_kc["keycloak namespace"]
                KC[Keycloak SSO]
            end
            Funnel[Tailscale Funnel]
        end
    
        subgraph CICD["CI/CD"]
            Forgejo[Forgejo Git]
            Woodpecker[Woodpecker CI]
            Harbor[Harbor Registry]
            ArgoCD[ArgoCD + Image Updater]
        end
    
        subgraph External
            KalshiAPI[Kalshi API v2]
        end
    
        Domain --> DNS --> Caddy
        Caddy --> Funnel --> Rails
        Browser --> Domain
        Rails --> CNPG
        Rails --> KC
        Rails --> KalshiAPI
        Forgejo --> Woodpecker
        Woodpecker --> Harbor
        Harbor --> ArgoCD
        ArgoCD --> Rails
    

    Components

    Component Purpose Notes
    Rails 8 + Solid Queue Application server and background jobs Namespace: kalshi-assistant, port 3000
    PostgreSQL 17 CNPG Shared database cluster 4 databases: primary, cache, queue, cable
    Keycloak SSO identity provider Separate realm for kalshi-assistant
    Caddy TLS termination for custom domain Auto Let's Encrypt, reverse proxies via Tailscale
    Tailscale Funnel Public HTTPS ingress to k3s kalshi-assistant.tail5b443a.ts.net
    GoDaddy DNS A record for prediction-assistant.com Managed via godaddy-tofu Terraform provider
    Woodpecker CI Build and test pipeline RuboCop lint, RSpec tests, Kaniko image build
    Harbor Container registry Project: kalshi-assistant, SHA-tagged images
    ArgoCD GitOps deployment Image Updater detects new SHA tags
    nginx dev proxy Dev tunnel to local machine Namespace: kalshi-dev
    Kalshi API v2 External trading API RSA-PSS SHA-256 auth, rate-limited

    Key Decisions

    • Three-repo model (pal-e-platform, pal-e-services, pal-e-deployments) — same proven pattern as landscaping-assistant
    • Port 3000 — Rails default, consistent with landscaping-assistant
    • Tailscale Funnel for prod ingress, Hetzner edge + Caddy for custom domain
    • Solid Queue replaces Sidekiq — no Redis pod needed
    • 4 PostgreSQL databases — Rails 8 Solid stack pattern on shared CNPG cluster
    • SOPS-encrypted secrets in pal-e-deployments for Kalshi credentials and Keycloak client secret

    URLs

    Environment URL
    Production (custom domain) https://prediction-assistant.com
    Production (Tailscale) https://kalshi-assistant.tail5b443a.ts.net
    Dev (Tailscale) https://kalshi-dev.tail5b443a.ts.net
    Local http://localhost:3000
    • arch-domain-kalshi-assistant — entities hosted in this deployment
    • arch-dataflow-kalshi-assistant — runtime flows between these components
    • project-kalshi-assistant — parent project page
    • service-onboarding-sop — SOP for provisioning new services
Project Page 1
  • Prediction Assistant project-prediction-assistant

    Vision

    AI-powered trading assistant for Kalshi prediction markets. Uses automated strategies (watchdog scanning, portfolio stacking with Option D hedging) to generate consistent returns while bounding worst-case loss. The system deploys a 4-bot architecture — Pregame Stacker, Late-Game Lock, Bulk Sweep, and Edge Learner — driven by a watchdog fan-out pattern that scans markets and dispatches opportunities to specialized bots. Users connect their Kalshi API credentials through an iOS app at prediction-assistant.com and the system trades on their behalf — they watch results live in the official Kalshi app.

    User Stories

    Key Story Note Role Success Metric
    portfolio-builder AI Portfolio Builder Trader (Lucas) Positive EV across 100+ trades, worst-case loss under 6%
    watchdog-trading Watchdog Trading Trader (Lucas) 85%+ win rate, $0.10–$0.15 avg profit per contract
    app-experience App Experience Consumer First automated trade within 5 min of connecting credentials
    credential-onboarding Credential Onboarding Consumer 90% of users complete setup without support
    platform-setup Platform Setup Developer (Lucas) CI/CD push-to-deploy in under 10 minutes
    landing-page Landing Page & Registration Consumer 80% of visitors who start registration complete it
    bot-marketplace Bot Marketplace Consumer User activates first bot within 2 minutes of login

    Architecture

    1. Domain Model — User, Credential, Strategy, Portfolio, Trade, Market entities
    2. Data Flow — watchdog scan loop, position monitoring, user auth flows
    3. Deployment — k3s + Tailscale Funnel + Hetzner edge + Caddy for prediction-assistant.com
    4. Rails — Rails 8 application architecture
    5. API — API layer design
    6. App — Application structure
    7. Postgres — Database schema and design
    8. Frontend — Frontend architecture
    9. Auth — Authentication and authorization
    10. Keycloak — Keycloak integration for prediction-assistant

    Key decisions:

    • Rails 8 with Solid Queue (no Sidekiq/Redis)
    • Kalshi API auth: RSA-PSS SHA-256 per-request signatures
    • Strategy polymorphism via strategy_type + jsonb parameters
    • Three-repo model: pal-e-platform, pal-e-services, pal-e-deployments

    Board

    board-prediction-assistant

    Status

    Project setup complete. All 16 tickets reviewed and in todo across 4 sprints. Bot architecture established (4 bots on watchdog fan-out). Strategy docs, API integration, and bot specs documented. PR #19 merged with bot specs and user stories. PR #20 merged with Late-Game Lock math fix and Edge Learner synthetic parlay clarification. Rails scaffold and infrastructure provisioning are next (Sprint 1).

    Milestones

    • 2026-07-02 — Strategy research and API exploration complete
    • 2026-07-03 — Bot architecture established, all tickets reviewed and moved to todo, PR #19 and #20 merged

    Repos

    Repo Platform Role Status
    ldraney/kalshi-assistant Forgejo Rails app + docs Active (docs only, Rails scaffold pending)
    ldraney/kalshi-assistant-ios Forgejo Turbo Native iOS shell Not yet created
Untyped 2
  • Bot Marketplace story-prediction-assistant-bot-marketplace

    As a registered user, I want to browse, configure, and activate trading bots with my own settings, so that I can automate my prediction market trading. See docs/user-stories/bot-marketplace.md for full spec.

  • Landing Page & Registration story-prediction-assistant-landing-page

    As a visitor to prediction-assistant.com, I want to understand what the platform does and register quickly, so that I can start using automated trading bots on Kalshi. See docs/user-stories/landing-page.md for full spec.

User Story 5
  • Platform Setup story-prediction-assistant-platform-setup

    story: Platform Setup

    Role

    Developer (Lucas)

    Key

    platform-setup

    Want

    As a developer, I want the full pal-e-platform infrastructure provisioned — namespace, Harbor, ArgoCD, CI/CD, DNS, monitoring, Keycloak, kustomize overlays

    So That

    So that the Rails app can be deployed, monitored, and accessed at kalshi-assistant.com through the standard three-repo pipeline

    Acceptance Criteria

    • Service onboarded in pal-e-services (namespace, Harbor, ArgoCD, funnel)
    • DNS A record and Caddy reverse proxy for kalshi-assistant.com
    • Kustomize overlays for prod and dev in pal-e-deployments
    • Keycloak realm, client, users, and login theme
    • Woodpecker CI pipeline with lint, test, build stages
    • Monitoring: AlertManager, blackbox probe, Grafana dashboard, PrometheusRules
    • End-to-end: push to main → CI green → image in Harbor → ArgoCD deploys → app live at kalshi-assistant.com

    Success Metric

    Full CI/CD pipeline operational: code push to production deploy in under 10 minutes

    • arch-deployment-kalshi-assistant — deployment diagram showing all infrastructure components
    • arch-dataflow-kalshi-assistant — auth flow through Keycloak
    • project-kalshi-assistant — parent project page
    • board-kalshi-assistant — project board
    • service-onboarding-sop — standard procedure for new services
  • Credential Onboarding story-prediction-assistant-credential-onboarding

    story: Credential Onboarding

    Role

    Consumer (new user)

    Key

    credential-onboarding

    Want

    As a new user, I want step-by-step guidance within the app to create my Kalshi account, generate API credentials, and paste them into the app

    So That

    So that I can connect my Kalshi account without needing external documentation or technical knowledge about API keys and RSA private keys

    Acceptance Criteria

    • In-app walkthrough guides user to create Kalshi account and deposit funds
    • Step-by-step instructions for generating API key ID and private key in Kalshi settings
    • Secure input fields for pasting API key ID and RSA private key
    • Validation that credentials work (test API call on save)
    • Credentials stored encrypted at rest
    • Clear error messages if credentials are invalid or expired

    Success Metric

    90% of users complete credential setup without contacting support

    • arch-domain-kalshi-assistant — User, Credential entities
    • arch-dataflow-kalshi-assistant — credential validation flow
    • arch-deployment-kalshi-assistant — secrets management for stored credentials
    • project-kalshi-assistant — parent project page
    • board-kalshi-assistant — project board
    • story-kalshi-assistant-app-experience — parent user experience story
  • Watchdog Trading story-prediction-assistant-watchdog-trading

    story: Watchdog Trading

    Role

    Trader (Lucas)

    Key

    watchdog-trading

    Want

    As a trader, I want a watchdog that continuously scans all same-day markets and automatically buys YES when odds cross 85%, then sells at 95% (take profit) or 80% (cut loss)

    So That

    So that I can capture reliable mid-game profits without needing pregame prediction, riding game state instead of forecasting outcomes

    Acceptance Criteria

    • Continuous polling of all active same-day markets via API
    • Auto-buy YES when any market's yes_bid crosses 85% threshold
    • Auto-sell at 95%+ (take profit) or below 80% (cut loss)
    • Configurable thresholds per strategy instance
    • Solid Queue background job for continuous scanning
    • Rate-limit-aware polling (stays within 20 reads/sec)
    • Tracks all positions and P&L in real time

    Success Metric

    85%+ win rate on watchdog trades with average profit of $0.10–$0.15 per contract

    • arch-domain-kalshi-assistant — Strategy, WatchdogConfig, Trade entities
    • arch-dataflow-kalshi-assistant — scan loop → threshold detection → order placement
    • project-kalshi-assistant — parent project page
    • board-kalshi-assistant — project board
  • App Experience story-prediction-assistant-app-experience

    story: App Experience

    Role

    Consumer (end user)

    Key

    app-experience

    Want

    As a user, I want to download the app from the App Store, connect my Kalshi API credentials, and have the AI run portfolio strategies that I can watch live in the Kalshi app

    So That

    So that I can benefit from automated trading strategies without needing to understand the math, while retaining full visibility through my Kalshi account

    Acceptance Criteria

    • App available on iOS App Store
    • User can log in via Keycloak (no redirect-based flow — native feel)
    • User can enter and save their Kalshi API key ID and private key
    • AI strategies run automatically after credentials are connected
    • User can see strategy projections and rationale before trades execute
    • All trades visible in the official Kalshi app (we don't duplicate portfolio UI)

    Success Metric

    User connects credentials and sees first automated trade within 5 minutes during active market hours

    • arch-domain-kalshi-assistant — User, Credential entities
    • arch-dataflow-kalshi-assistant — credential storage → API auth → trade execution
    • arch-deployment-kalshi-assistant — iOS pipeline, App Store distribution
    • project-kalshi-assistant — parent project page
    • board-kalshi-assistant — project board
  • AI Portfolio Builder story-prediction-assistant-portfolio-builder

    story: AI Portfolio Builder

    Role

    Trader (Lucas)

    Key

    portfolio-builder

    Want

    As a trader, I want an AI system that analyzes Kalshi markets, constructs optimized portfolios using stacking and NO-contract hedging, and executes trades via the API

    So That

    So that I can achieve consistent returns by combining high-confidence picks with bounded downside risk, overcoming the house edge through informational edge and volume

    Acceptance Criteria

    • System scans all same-day markets and identifies 85%+ confidence picks
    • Portfolio constructed using Option D sizing (83% YES / 17% NO hedge)
    • Worst-case loss bounded to ~6% of portfolio value
    • Trades executed via Kalshi API with RSA-PSS SHA-256 auth
    • Strategy codified as database schema with configurable parameters
    • System respects API rate limits (20 reads/sec, 10 writes/sec)

    Success Metric

    Positive EV across 100+ trades with worst-case single-portfolio loss under 6%

    • arch-domain-kalshi-assistant — Strategy, Trade, Market, Portfolio entities
    • arch-dataflow-kalshi-assistant — market scan → strategy evaluation → trade execution flow
    • project-kalshi-assistant — parent project page
    • board-kalshi-assistant — project board
Board 1