Nemo

nemo forgejo

Notes

Doc 1
  • Architecture: nemo-agent arch-nemo-agent

    nemo-agent Architecture

    Architecture ID used in board labels: arch:nemo-agent

    Components

    Component Language Framework Responsibility
    Webhook server Python FastAPI Receive Postmark inbound POST, parse email, route to project agent
    Agent loop Python anthropic SDK Orchestrate workflow via Claude Opus 4.6 tool use
    Project configs Markdown Workflow guides per client project

    Data Flow

    Postmark inbound → FastAPI /inbound → parse email → route to project agent
    → Claude Opus 4.6 (tool use) → client API calls (Rails JSON API, Stripe)
    → Postmark outbound → email to admin/parent

    Deployment

    • Namespace: nemo
    • Image: nemo/app (Harbor)
    • Port: 8000 (FastAPI/uvicorn)
    • Exposed via Tailscale funnel for Postmark webhook delivery
    • Overlay: pal-e-deployments/overlays/nemo/prod

    Authentication

    Service Method
    Anthropic API API key (ANTHROPIC_API_KEY)
    Postmark Server token + webhook signature
    Rails API Bearer token (API_TOKEN)
    Stripe API key (STRIPE_SECRET_KEY)
    • project-nemo — project page
    • Repo docs: docs/architecture.md in ldraney/nemo
Project Page 1
  • Project: Nemo project-nemo

    Overview

    AI email agent. Receives inbound email via Postmark webhook, processes it with Claude (Opus 4.6) via the Anthropic API, and takes action through client project APIs.

    Nemo is the brain layer — client apps (Rails, FastAPI, etc.) are the hands. Each client project gets a projects/<project>/ directory with a workflow guide that teaches Nemo how to use that project's API.

    Architecture

    Python service (FastAPI + Anthropic SDK) deployed to the Hetzner k3s cluster. Two responsibilities:

    • Webhook receiver — accepts Postmark inbound email POST, routes to project agent
    • Agent loop — uses Claude Opus 4.6 to process emails and execute workflows with tool use

    Email flow: Postmark inbound → Nemo webhook → Claude decides action → calls client API → sends via Postmark outbound.

    Repos

    • ldraney/nemo — Python service repo (Forgejo)

    User Stories

    story:billing-follow-up

    As Marcus (admin), I want an AI agent that emails me billing drafts for each player and waits for my approval before sending, so that I can process billing follow-ups via email without needing to log into any app.

    First project. 17 remaining players owe money for tournament fees and monthly dues. Nemo pulls data from the westside-basketball Rails API, drafts emails, gets Marcus's approval via Postmark email thread, sends to parents.

    Board

    Board: board-nemo

    Key People

    • Lucas Draney — superadmin, monitors Nemo's operation, handles technical issues
    • Marcus Draney — admin, approves/adjusts billing emails via email reply
Review 2
  • Verdict: NEEDS_REFINEMENT

    Re-review after refinement. Prior review: review-1903-2026-07-25. All [BODY] issues from the prior review have been resolved. Remaining issues are [SCOPE] items only -- companion artifacts that need to be created.

    Prior Review Resolution

    • [x] [BODY] Resolve data access approach -- RESOLVED. Issue now explicitly commits to JSON API on Rails with 5 named endpoints. Prerequisite documented in "What Must Be Built" section with code blocks, routes, and env var requirements.
    • [x] [BODY] Resolve email sending approach -- RESOLVED. Issue explicitly states "Email sending uses Postmark outbound (not Gmail/ActionMailer). Nemo sends directly via Postmark API, not through the Rails app." Repeated in Constraints.
    • [x] [BODY] Fix ParentMailer#billing_followup reference -- RESOLVED. Issue now correctly lists the 3 public mailer methods (announcement, payment_reminder, quick_send) and explicitly states "There is NO billing_followup method. Nemo does NOT use Rails mailers."
    • [x] [BODY] AC vagueness for edge cases -- IMPROVED. AC #10 now enumerates specific edge cases: "no-show players, disputed charges, players who left program."
    • [ ] [SCOPE] Create project-nemo page -- STILL MISSING (404)
    • [ ] [SCOPE] Create arch-nemo-agent note -- STILL MISSING (404)
    • [x] [DECOMPOSE] Owner explicitly chose to keep as single ticket -- ACKNOWLEDGED, respected

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, first ticket for Nemo project
    • [x] Repo -- ldraney/nemo
    • [x] User Story -- well-formed (As Marcus, I want an AI agent that emails me billing drafts...)
    • [x] Context -- thorough, includes key architectural decisions and "What Exists Today" inventory
    • [x] File Targets -- 13 files to create, 3 directories excluded with reasons
    • [x] Feature Flag -- none
    • [x] Acceptance Criteria -- 13 criteria
    • [x] Test Expectations -- 5 test items + run command
    • [x] Constraints -- 8 constraints listed
    • [x] Checklist -- 4 items
    • [x] Related -- 6 references

    All required template sections present. The issue is notably thorough, with additional sections ("What Exists Today in the Rails App" and "What Must Be Built") that provide excellent context for the implementing agent.

    Traceability

    • [x] story:billing-follow-up label -- present on board item
    • [ ] story note MISSING -- project-nemo page does not exist (404). [SCOPE] Create project page project-nemo with user-stories section containing the billing-follow-up story.
    • [x] arch:nemo-agent label -- present on board item
    • [ ] arch note MISSING -- arch-nemo-agent note does not exist (404). [SCOPE] Create architecture note arch-nemo-agent. The nemo repo's docs/architecture.md has detailed system diagrams and component descriptions to derive from.
    • [x] Forgejo issue -- ldraney/nemo#1, state: open

    File Targets

    • [x] src/__init__.py -- to create; standard package init
    • [x] src/webhook.py -- to create; repo currently docs-only, valid path
    • [x] src/agent.py -- to create; consistent with architecture doc
    • [x] src/tools.py -- to create; consistent with architecture doc
    • [x] src/config.py -- to create; consistent with architecture doc
    • [x] Dockerfile -- to create; standard containerized service
    • [x] pyproject.toml -- to create; standard Python project
    • [x] .pre-commit-config.yaml -- to create; matches platform convention
    • [x] .woodpecker/ci.yaml -- to create; matches platform CI convention
    • [x] tests/__init__.py -- to create; test package init
    • [x] tests/test_webhook.py -- to create; webhook parsing tests
    • [x] tests/test_approval.py -- to create; approval parser tests
    • [x] tests/test_fixes.py -- to create; standard fixes logic tests
    • [x] docs/ -- excluded, already exists in repo (verified)
    • [x] projects/ -- excluded, workflow.md exists in repo (verified)
    • [x] README.md -- excluded, already exists (verified)

    All file targets valid for a greenfield service in a docs-only repo.

    Rails App References Verified

    The "What Exists Today" section makes specific claims about the westside-basketball codebase. All verified against the actual code:

    • [x] WebhooksController inherits from ActionController::Base -- confirmed
    • [x] No API controllers exist (app/controllers/api/ directory absent) -- confirmed
    • [x] No API_TOKEN env references anywhere in the app -- confirmed
    • [x] Player model: has_many :orders, :payment_links, :player_tournaments; columns stripe_customer_id, subscription_status, monthly_fee, contract_status, parent_id -- all confirmed in schema
    • [x] Parent model: has_many :players; columns name, email, phone -- confirmed
    • [x] Order model: belongs_to :player, :parent, :product; columns amount_cents, status (orderstatus enum) -- confirmed. Minor: ticket says "pending/paid/canceled" but enum also includes "refunded"
    • [x] PaymentLink model: belongs_to :player, :parent, :order (optional); columns description, amount_cents, status, stripe_payment_link_id, stripe_payment_link_url -- confirmed
    • [x] PlayerTournament model: belongs_to :player, :tournament; validates payment_status against PAYMENT_STATUSES (pending/paid_stripe/paid_cash/paid_venmo/waived) -- confirmed
    • [x] ParentMailer: 3 public methods (announcement, payment_reminder, quick_send), no billing_followup -- confirmed
    • [x] jbuilder gem installed but unused -- confirmed
    • [x] No rack-cors gem -- confirmed
    • [x] TENANT_SLUG = "westside-kings-queens" in ApplicationController -- confirmed
    • [x] StripeClient and GmailClient services exist -- confirmed

    Repo Placement

    Issue filed on ldraney/nemo. All file targets are in the nemo repo. Cross-repo Rails API work is explicitly documented as a prerequisite in the "What Must Be Built" section with full controller code, route definitions, and env var requirements. The Checklist includes "Rails API prerequisite ticket created on westside-basketball repo." This is correct placement.

    However, the Rails API prerequisite ticket does NOT yet exist on westside-basketball (checked all 19 open issues -- none match).

    Dependencies

    • Only item on board-nemo; no board-level blockers or blocked items.
    • Cross-repo prerequisite (documented but not created): 5 JSON API endpoints + BaseController + API_TOKEN env var on westside-basketball. The issue documents these thoroughly but the actual Forgejo issue has not been created yet.
    • Operational dependencies (documented in Constraints): Postmark account, Anthropic API key, Stripe API key, k8s namespace -- not code dependencies, deployment prerequisites.

    Acceptance Criteria

    13 acceptance criteria. All are testable:

    • [x] AC 1 (POST /inbound parses Postmark JSON) -- testable via webhook mock
    • [x] AC 2 (Agent pulls billing data via Rails API) -- testable with mock; depends on Rails prerequisite
    • [x] AC 3 (Standard fixes: rename dues, cancel dupes, add missing fees) -- testable with mock data
    • [x] AC 4 (Draft billing email with itemized table) -- testable
    • [x] AC 5 (Send test email to Marcus via Postmark) -- testable
    • [x] AC 6 (Parse Marcus's approval reply) -- testable, approval language explicitly enumerated
    • [x] AC 7 (Send to parent only after explicit approval) -- testable
    • [x] AC 8 (One player at a time, sequential) -- testable
    • [x] AC 9 (Resume from persistent state file) -- testable
    • [x] AC 10 (Flag edge cases) -- testable, specific cases enumerated
    • [x] AC 11 (Postmark error handling with retry/backoff) -- testable
    • [x] AC 12 (Ruff lint + format in CI) -- testable
    • [x] AC 13 (pytest tests pass) -- testable

    One minor note: the approval parsing constraint is well-specified -- "send it"/"approved"/naming the recipient count as approval; "Push it"/"let's go"/"ASAP" do NOT. This is a critical safety gate that is clearly testable.

    Blast Radius

    • Nemo is a new standalone service; blast radius within its own repo is contained.
    • Cross-repo impact on westside-basketball is limited to adding a new API namespace (no existing code modified).
    • No existing Postmark integration in the infrastructure -- net-new.
    • Documentation inconsistency in nemo repo: projects/westside-billing-agent/workflow.md still references Gmail API, ParentMailer#billing_followup, POST /api/v1/emails/send, and kubectl exec approach. docs/architecture.md diagram has stale "send email" label on the Rails connection and mentions "Gmail" in the intro. These contradict the issue's explicit Postmark approach. An implementing agent reading these docs could get confused.

    Decomposition Assessment

    Thresholds exceeded but owner chose single ticket:

    • 13 file targets in 1 repo -- exceeds 3+ threshold (but single repo, not >2 repos)
    • 13 acceptance criteria -- exceeds 5 AC threshold
    • Estimated agent work far exceeds 5 minutes -- full greenfield service
    • Owner explicitly chose to keep as single detailed ticket rather than decompose

    The issue's level of detail (inline code blocks, exact routes, column-level model descriptions) partially compensates for its size. An agent has enough specificity to implement without ambiguity, even though the scope is large. Respecting owner's decision.

    Recommendation

    • [SCOPE] Create project page project-nemo with user-stories section containing the billing-follow-up story entry. (Carried forward from prior review.)
    • [SCOPE] Create architecture note arch-nemo-agent for the agent component. Derive from docs/architecture.md in the nemo repo. (Carried forward from prior review.)
    • [SCOPE] Create the Rails API prerequisite ticket on westside-basketball. The issue body contains complete spec material (controller code, routes, env var) -- this should be extracted into a separate Forgejo issue before work starts.
    • [SCOPE] Update projects/westside-billing-agent/workflow.md and docs/architecture.md in the nemo repo to reflect the Postmark decision. Currently they reference Gmail API, ParentMailer#billing_followup, and POST /api/v1/emails/send, which contradict the issue's Postmark approach. This should happen before an agent starts implementation to avoid confusion.
  • Verdict: NEEDS_REFINEMENT

    Template Completeness

    • [x] Type -- Feature
    • [x] Lineage -- Standalone, first ticket for Nemo project
    • [x] Repo -- ldraney/nemo
    • [x] User Story -- well-formed (As Marcus, I want...)
    • [x] Context -- thorough background on the 28-player billing campaign
    • [x] File Targets -- 8 files to create, 2 files excluded
    • [x] Feature Flag -- none
    • [x] Acceptance Criteria -- 9 criteria
    • [x] Test Expectations -- 4 test items + run command
    • [x] Constraints -- 6 constraints listed
    • [x] Checklist -- present
    • [x] Related -- present

    All required template sections are present.

    Traceability

    • [x] story:billing-follow-up label -- present on board item
    • [ ] story note MISSING -- project-nemo page does not exist (404). [SCOPE] Create project page project-nemo with user-stories section containing the billing-follow-up story.
    • [x] arch:nemo-agent label -- present on board item
    • [ ] arch note MISSING -- arch-nemo-agent note does not exist (404). [SCOPE] Create architecture note arch-nemo-agent for the agent component.
    • [x] Forgejo issue -- ldraney/nemo#1, state: open

    File Targets

    • [x] src/webhook.py -- to create; repo is currently docs-only, path is valid
    • [x] src/agent.py -- to create; consistent with architecture doc
    • [x] src/tools.py -- to create; consistent with architecture doc
    • [x] src/config.py -- to create; consistent with architecture doc
    • [x] Dockerfile -- to create; standard for containerized service
    • [x] pyproject.toml -- to create; standard Python project file
    • [x] .woodpecker/ci.yaml -- to create; matches platform CI convention
    • [x] .pre-commit-config.yaml -- to create; matches platform convention
    • [x] docs/ -- excluded (already exists in repo)
    • [x] projects/westside-billing-agent/workflow.md -- excluded (exists in repo)

    All file targets are valid for a greenfield service in a docs-only repo.

    Repo Placement

    Issue is filed on ldraney/nemo and all file targets are in the nemo repo -- correct. However, there is an undocumented cross-repo dependency:

    • AC #2 references GET /api/v1/players/:id/billing but the westside-basketball Rails app has NO JSON API endpoints at all. The routes.rb has no api/v1 namespace and no API controllers exist.
    • The workflow doc (projects/westside-billing-agent/workflow.md) explicitly lists "JSON API endpoints" under "What Is NOT Built" and proposes two options: (A) JSON API on Rails, or (B) Direct API access via kubectl/Stripe/Gmail.
    • The ticket implicitly assumes Option A without creating a companion westside-basketball ticket for the API endpoints.
    • AC #4 references ParentMailer#billing_followup but that method does not exist. ParentMailer only has announcement, payment_reminder, and quick_send.

    The ticket must either: (a) add a cross-repo dependency ticket on westside-basketball for JSON API + mailer work, or (b) rewrite the approach to use Option B (direct API access) and clarify which mailer method to use.

    Dependencies

    • Only item on board-nemo; no board-level blockers or blocked items.
    • Cross-repo dependency (undocumented): Rails JSON API endpoints (/api/v1/players/:id/billing, PATCH /api/v1/orders/:id, POST /api/v1/orders, POST /api/v1/emails/send, GET /api/v1/emails/inbox) do not exist in westside-basketball. No ticket exists to create them.
    • Cross-repo dependency (undocumented): ParentMailer#billing_followup does not exist in westside-basketball. Only announcement, payment_reminder, quick_send exist.
    • Operational dependencies: Postmark account + DNS setup, Anthropic API key, Stripe API key, k8s namespace creation -- not code dependencies but need to exist before deployment.

    Acceptance Criteria

    9 acceptance criteria listed. Assessment:

    • AC #1 (Postmark triggers agent) -- testable via webhook mock
    • AC #2 (Agent pulls from Rails JSON API) -- BLOCKED: API does not exist in the Rails app
    • AC #3 (Standard fixes) -- testable with mock data
    • AC #4 (Draft billing email via Postmark) -- references nonexistent billing_followup mailer; also, the architecture doc says Nemo sends via Postmark outbound, but the workflow doc references ParentMailer (Rails Gmail delivery). These contradict each other.
    • AC #5-6 (Marcus approval flow) -- testable
    • AC #7 (One player at a time) -- testable
    • AC #8 (Persistent state on restart) -- testable
    • AC #9 (Custom email handling for edge cases) -- vague; what edge cases specifically? "no-show, disputed, left program" are mentioned in Constraints but not mapped to specific AC behaviors

    Blast Radius

    • Nemo is a new standalone service, so blast radius within its own repo is contained.
    • Cross-repo impact on westside-basketball is the primary concern: JSON API endpoints, mailer methods, and potentially auth token setup.
    • No existing Postmark integration anywhere in the infrastructure -- this is net-new infrastructure.
    • Email sending conflict: architecture.md says Nemo sends via Postmark outbound, workflow.md says emails go through ParentMailer (Rails + Gmail). The ticket must pick one approach.

    Decomposition Assessment

    NEEDS DECOMPOSITION -- route to skill-decompose-ticket

    • 8 file targets in 1 repo -- exceeds the 3+ file threshold
    • 9 acceptance criteria -- exceeds the 5 AC threshold
    • Estimated agent work far exceeds 5 minutes -- building an entire FastAPI service with Claude agent loop, Postmark webhook handling, tool definitions, persistent state, Dockerfile, CI pipeline, and pre-commit config is a multi-hour effort
    • Cross-repo dependency -- Rails JSON API and/or mailer work needs a separate ticket on westside-basketball

    Suggested decomposition axes:

    1. Skeleton service (FastAPI webhook + config + Dockerfile + CI + pre-commit) -- infra only, no agent logic
    2. Agent loop core (agent.py + tools.py) -- Claude API integration with tool use
    3. Postmark integration (inbound parsing + outbound sending + signature validation)
    4. Billing workflow logic (standard fixes, email drafting, approval parsing)
    5. State management (persistent state file, resume on restart)
    6. Cross-repo: westside-basketball JSON API endpoints (separate repo ticket)

    Recommendation

    • [SCOPE] Create project page project-nemo with user-stories section containing the billing-follow-up story entry.
    • [SCOPE] Create architecture note arch-nemo-agent for the agent component. The architecture.md in the repo is detailed -- this can be derived from it.
    • [BODY] Resolve the data access approach: either commit to Option A (JSON API on Rails, which requires a cross-repo dependency ticket on westside-basketball) or Option B (direct API access via Stripe/kubectl). Update AC #2 accordingly.
    • [BODY] Resolve the email sending approach: architecture.md says Postmark outbound, workflow.md says ParentMailer (Rails + Gmail). Pick one and update AC #4 and the File Targets section.
    • [BODY] Fix AC #4: ParentMailer#billing_followup does not exist. Either reference announcement/quick_send or add a cross-repo dependency to create the method.
    • [BODY] AC #9 (custom email handling) is too vague for agent verification. Enumerate the specific edge cases and expected agent behavior for each.
    • [DECOMPOSE] 8 file targets and 9 AC across a full greenfield service far exceed the 5-minute rule. Route to skill-decompose-ticket for sub-ticket creation. Suggested decomposition: skeleton service, agent loop, Postmark integration, billing workflow, state management, and cross-repo Rails API work.
Board 1
  • Nemo board-nemo

    No content

Repos 1
  • nemo
    active