# RISE Mini API Contract

> Canonical specification for all RISE Mini backend endpoints.
> Both tech stacks (Next.js and Spring Boot) MUST implement these contracts identically.
> This document is the **scoring reference** for the workshop.

## Common Conventions

- **Content-Type**: `application/json` for all responses
- **Current User**: Hardcoded as `user-alice` (no auth)
- **Error Shape**: `{ "error": "string", "message": "string" }`

---

## Endpoints

### 1. `GET /api/scores`

Returns assessment scores for the current user across all 6 dimensions.

**Query Parameters**: None

**Response** `200 OK`:

```json
{
  "userId": "user-alice",
  "scores": [
    {
      "dimension": "ai_literacy",
      "label": "AI Literacy",
      "value": 0.72,
      "responded": true
    },
    {
      "dimension": "workflow",
      "label": "Workflow/SDLC",
      "value": 0.58,
      "responded": true
    },
    {
      "dimension": "tooling",
      "label": "Tooling",
      "value": 0.85,
      "responded": true
    },
    {
      "dimension": "governance",
      "label": "Governance",
      "value": 0.0,
      "responded": false
    },
    {
      "dimension": "collaboration",
      "label": "Collaboration",
      "value": 0.63,
      "responded": true
    },
    {
      "dimension": "business_impact",
      "label": "Business Impact",
      "value": 0.0,
      "responded": false
    }
  ],
  "overallScore": 0.46
}
```

**Notes**:
- The 6 dimensions are: `ai_literacy`, `workflow`, `tooling`, `governance`, `collaboration`, `business_impact`
- `responded: false` means the user skipped this dimension (N/A)
- `overallScore` is the average of all dimension `value` fields
- **Bug #2 is planted here**: the buggy implementation includes N/A scores (value=0.0) in the average, deflating the overall score. The correct implementation should only average dimensions where `responded=true`.

**Error** `500`:
```json
{ "error": "Failed to fetch scores" }
```

---

### 2. `GET /api/lessons`

Returns lessons with optional filters and completion status for the current user.

**Query Parameters**:

| Param | Type | Description |
|-------|------|-------------|
| `dimension` | string | Filter by dimension (e.g. `ai_literacy`). `all` or omitted = no filter |
| `stage` | string | Filter by stage (e.g. `exploring`). `all` or omitted = no filter |
| `module` | string | Filter by module name |

**Response** `200 OK`:

```json
[
  {
    "id": "lesson-1",
    "title": "Understanding AI Capabilities",
    "description": "Learn what modern AI tools can and cannot do...",
    "dimension": "ai_literacy",
    "stage": "exploring",
    "duration": 10,
    "module": "AI Fundamentals",
    "completed": true,
    "completionCount": 1
  }
]
```

**Notes**:
- Results are ordered by `module` (asc), then `title` (asc)
- `completed` is `true` if the current user has a `LessonCompletion` record
- `completionCount` is the `count` field from the completion record (0 if not completed)
- Lesson 12 contains an XSS payload in `description` (Bug #4 - a seed data issue, not an API bug)

**Error** `500`:
```json
{ "error": "Failed to fetch lessons" }
```

---

### 3. `POST /api/lessons/:id/complete`

Marks a lesson as completed for the current user. If already completed, increments the completion count.

**Path Parameters**:

| Param | Type | Description |
|-------|------|-------------|
| `id` | string | Lesson ID (e.g. `lesson-1`) |

**Request Body**: None

**Response** `200 OK` (first completion):

```json
{
  "message": "Lesson marked as complete",
  "count": 1
}
```

**Response** `200 OK` (subsequent completions):

```json
{
  "message": "Lesson completion updated",
  "count": 2
}
```

**Response** `404 Not Found`:
```json
{ "error": "Lesson not found" }
```

**Notes**:
- **Bug #5 is planted here**: the buggy implementation uses a read-then-write pattern without a transaction. It reads the current count, sleeps 100ms (to widen the race window), then writes the incremented count. Rapid double-clicks create duplicate completions or incorrect counts.
- The correct fix is to use an atomic upsert or wrap in a transaction.

**Error** `500`:
```json
{ "error": "Failed to mark lesson complete" }
```

---

### 4. `GET /api/compare` (Feature Gap)

Compare current user's scores against team or organisation averages.

**Status**: `501 Not Implemented` (this is a Feature Sprint challenge)

**Query Parameters**:

| Param | Type | Description |
|-------|------|-------------|
| `scope` | string | `team` (same team only) or `org` (all users). Default: `team` |

**Current Response** `501`:
```json
{
  "error": "Not Implemented",
  "message": "Team comparison feature is not yet available. This is your Feature Sprint challenge!",
  "hint": "Implement this endpoint to compare a user's scores against the average scores of all users (org) or users in the same team."
}
```

**Expected Response** `200 OK` (when implemented):
```json
{
  "user": [
    { "dimension": "ai_literacy", "label": "AI Literacy", "value": 0.72, "responded": true }
  ],
  "comparison": [
    { "dimension": "ai_literacy", "label": "AI Literacy", "value": 0.63, "responded": true }
  ],
  "scope": "team"
}
```

---

### 5. `GET /api/recommend` (Feature Gap)

Get personalised lesson recommendations based on the user's weakest dimensions.

**Status**: `501 Not Implemented` (this is a Feature Sprint challenge)

**Query Parameters**: None

**Current Response** `501`:
```json
{
  "error": "Not Implemented",
  "message": "Lesson recommendations feature is not yet available. This is your Feature Sprint challenge!",
  "hint": "Implement this endpoint to recommend lessons based on the user's lowest dimension scores."
}
```

**Expected Response** `200 OK` (when implemented):
```json
{
  "recommendations": [
    {
      "lesson": {
        "id": "lesson-16",
        "title": "Measuring AI Productivity",
        "description": "...",
        "dimension": "business_impact",
        "stage": "exploring",
        "duration": 10,
        "module": "Business Impact"
      },
      "reason": "Your Business Impact score (0.0) is your lowest dimension",
      "gapScore": 1.0
    }
  ]
}
```

**Notes**:
- Should return top 5 recommendations
- `gapScore` = `1.0 - userDimensionScore` (higher = bigger gap = more urgent)
- Only recommend lessons the user hasn't completed
- Order by `gapScore` descending, then by lesson `stage` (exploring first)

---

## Dimensions Reference

| Key | Label |
|-----|-------|
| `ai_literacy` | AI Literacy |
| `workflow` | Workflow/SDLC |
| `tooling` | Tooling |
| `governance` | Governance |
| `collaboration` | Collaboration |
| `business_impact` | Business Impact |

## Stages Reference

| Key | Label |
|-----|-------|
| `exploring` | Exploring |
| `building` | Building |
| `applying` | Applying |
