# Jira Image Requests

LLMS index: [llms.txt](/llms.txt) | Full content: [llms-full.txt](/llms-full.txt)

---

AWS Lambda proxy that serves Hummingbird Image Request issues from Jira (with
DynamoDB caching) and creates Stories/Epics from the catalog request form.

## Features

- **Public list**: GET `/image-requests` returns Ready Image Request issues
- **Form submit**: POST `/image-requests` creates a version Story. An Epic is
  created only when a second request uses the same image name; later requests
  reuse that Epic. A first-time name is Story-only.
- **Customer scoring**: Writes the Customer criterion on the Epic from paid
  email domains (placeholder until subscriber login)
- **Total scoring**: Maintained by Jira Automation from manual triage fields

## Prerequisites

- AWS CLI with permissions for Lambda, API Gateway, DynamoDB, SSM, CloudFormation
- Podman or Docker for SAM build
- Jira bot token in SSM (SecureString)
- HUM project custom fields for scoring (see below)

## Deployment

```bash
make jira-image-requests/build
make jira-image-requests/deploy
```

## Configuration

| Parameter / env            | Description                                                                 |
|----------------------------|-----------------------------------------------------------------------------|
| `ResourcePrefix`           | Prefix for AWS resource names                                               |
| `JiraTokenParameter`       | SSM parameter name for the Jira API token                                   |
| `CacheTtlSeconds`          | Cache TTL for the public list (default 300)                                 |
| `CorsAllowOrigin`          | Allowed CORS origin                                                         |
| `ScoringCustomerField`     | Jira custom field id for Customer (`customfield_…`); empty skips write      |
| `PaidCustomerDomains`      | Comma-separated email domains scored as paid (e.g. `acme.com,contoso.com`)  |
| `SentryDsn`                | Optional Sentry DSN                                                         |

## Image request scoring

Triage scores live on the **Image Request Epic**, not on customer-facing Stories.

### Custom fields (HUM Epics)

Create (or map) these fields, then record each `customfield_XXXXX` id:

| Field                    | Type / values                         | Who sets it                          |
|--------------------------|---------------------------------------|--------------------------------------|
| IR Legal                 | Select: Y / N                         | Manual                               |
| IR Competitive           | Number (typically 0–1)                | Manual                               |
| IR Marketable            | Number                                | Manual                               |
| IR Supportability        | Number                                | Manual                               |
| IR Upstream Health       | Number                                | Manual                               |
| IR Level of Effort       | Number: 3 easy, 2 medium, 1 hard      | Manual                               |
| IR Technical Feasibility | Number: 0 blocking, 1 feasible        | Manual                               |
| IR Portfolio Conflicts   | Number: 0 conflict, 1 good            | Manual                               |
| IR Customer              | Number: 0 or 1                        | Lambda (`ScoringCustomerField`)      |
| IR Total                 | Number                                | Jira Automation only                 |

**Gate:** Legal must be **Y** to continue scoring. Competitive, Marketable, and
Supportability / Maturity are also expected to be **1** before a meaningful
Total (team convention from the triage spreadsheet).

### Lambda: Customer criterion

When a POST creates or reuses an Epic (second or later request for a name),
the Lambda:

1. Loads Stories under the Epic
2. Sets Customer to **1** if any submitter email domain is in
   `PaidCustomerDomains`, otherwise **0**
3. Writes the value to `ScoringCustomerField` when configured

A first request for a name does not create an Epic, so Customer is not written
until a second request for that name arrives.

When subscriber login lands, replace the domain list with real subscription
status.

### Jira Automation: Total and Legal flag

Configure two rules in Jira (Project settings → Automation). Use the real field
names/ids from your instance.

#### Rule 1 — Sum Total when Legal is Y

- **Trigger:** Field value changed for any of: Competitive, Marketable,
  Supportability, Upstream Health, Level of Effort, Technical Feasibility,
  Portfolio Conflicts, Customer (and optionally Legal)
- **Condition:** Issue type = Epic AND component = Image Request AND
  IR Legal = Y
- **Action:** Edit issue → set IR Total to the sum of the numeric criteria
  (exclude Legal). Example smart value shape:

```text
{{#=}}
{{issue.IR Competitive}} + {{issue.IR Marketable}} + {{issue.IR Supportability}} +
{{issue.IR Upstream Health}} + {{issue.IR Level of Effort}} +
{{issue.IR Technical Feasibility}} + {{issue.IR Portfolio Conflicts}} +
{{issue.IR Customer}}
{{/}}
```

Use your instance’s smart-value field keys (often `customfield_XXXXX`).

- **Optional action:** Remove label `ir-legal-blocked` when Legal is Y

#### Rule 2 — Flag when Legal is not Y

- **Trigger:** IR Legal changed
- **Condition:** Issue type = Epic AND component = Image Request AND
  IR Legal is empty OR IR Legal = N
- **Actions:**
  1. Clear IR Total (or set to empty)
  2. Add label `ir-legal-blocked`

Jira Automation recalculates Total whenever triage fields change — no webhook
or Lambda rescoring path is required.

## Development

```bash
make jira-image-requests/setup
make test
```

## License

GPL-3.0-or-later
