# Engineering Backlog Bridge — DAARION.city

## Overview

The **Engineering Backlog Bridge** converts Risk/Pressure digest signals into a
**managed, structured backlog** of engineering work items. It closes the loop:

```
observe (Risk/Pressure) → decide (digest) → plan (backlog) → enforce (gates)
```

No LLM. Fully deterministic. Policy-driven. Idempotent (weekly dedupe).

---

## Data Model

### BacklogItem

| Field          | Type     | Description |
|----------------|----------|-------------|
| `id`           | string   | `bl_<hex12>` |
| `created_at`   | ISO ts   | When created |
| `updated_at`   | ISO ts   | Last modification |
| `env`          | string   | `prod` / `staging` / `dev` |
| `service`      | string   | DAARION service name |
| `category`     | enum     | `arch_review`, `refactor`, `slo_hardening`, `cleanup_followups`, `security` |
| `title`        | string   | Short human-readable label |
| `description`  | string   | Bullet-list of signals + context |
| `priority`     | enum     | `P0` .. `P3` |
| `status`       | enum     | See Workflow below |
| `owner`        | string   | `oncall` / `cto` / team name |
| `due_date`     | YYYY-MM-DD | Computed from category `due_days` |
| `source`       | string   | `risk` / `pressure` / `digest` / `manual` |
| `dedupe_key`   | string   | `platform_backlog:{YYYY-WW}:{env}:{service}:{category}` |
| `evidence_refs`| dict     | `alerts[]`, `incidents[]`, `release_checks[]`, `artifacts[]`, `followups[]` |
| `tags`         | list     | `["auto", "week:2026-W08", "rule:arch_review_required"]` |
| `meta`         | dict     | Free-form metadata |

### BacklogEvent (timeline)

| Field      | Type   | Description |
|------------|--------|-------------|
| `id`       | string | `ev_<hex12>` |
| `item_id`  | string | FK to BacklogItem |
| `ts`       | ISO ts | Event timestamp |
| `type`     | enum   | `created`, `status_change`, `comment`, `auto_update` |
| `message`  | string | Human-readable description |
| `actor`    | string | Who triggered the event |
| `meta`     | dict   | Old/new status, rule name, etc. |

---

## Workflow

```
open ──► in_progress ──► done
  │            │
  │            ▼
  └──► blocked ──► in_progress
  │
  └──► canceled (terminal)
```

| From         | Allowed targets               |
|--------------|-------------------------------|
| `open`       | in_progress, blocked, canceled |
| `in_progress`| blocked, done, canceled        |
| `blocked`    | open, in_progress, canceled    |
| `done`       | (none — terminal)              |
| `canceled`   | (none — terminal)              |

Transitions are enforced by `validate_transition()` in `backlog_store.py`.

---

## Auto-generation Rules

Rules are evaluated **per-service** from `config/backlog_policy.yml`.
All conditions in `when` must hold (AND logic). First matching rule per
category wins (no duplicate categories per service per week).

| Rule name               | Trigger condition                           | Category           | Priority |
|-------------------------|---------------------------------------------|--------------------|----------|
| `arch_review_required`  | `pressure_requires_arch_review: true`       | `arch_review`      | P1 / 14d |
| `high_pressure_refactor`| `pressure_band` AND `risk_band` ∈ high/critical | `refactor`     | P1 / 21d |
| `slo_violations`        | `risk_has_slo_violations: true`             | `slo_hardening`    | P2 / 30d |
| `followup_backlog`      | `followups_overdue > 0`                     | `cleanup_followups`| P2 / 14d |

---

## Dedupe Logic

Each item has a `dedupe_key`:

```
platform_backlog:{YYYY-WW}:{env}:{service}:{category}
```

`upsert()` uses this key:
- **First run of week** → creates the item.
- **Subsequent runs** → updates title/description/evidence_refs (preserves status/owner).

This means weekly re-generation is safe and idempotent.

---

## API

### HTTP Endpoints

| Method | Path                                | RBAC                   | Description |
|--------|-------------------------------------|------------------------|-------------|
| GET    | `/v1/backlog/dashboard?env=prod`    | `tools.backlog.read`   | Status/priority/overdue summary |
| GET    | `/v1/backlog/items`                 | `tools.backlog.read`   | Filtered item list |
| GET    | `/v1/backlog/items/{id}`            | `tools.backlog.read`   | Single item + event timeline |
| POST   | `/v1/backlog/generate/weekly`       | `tools.backlog.admin`  | Trigger weekly auto-generation |

Query params for `/v1/backlog/items`:
`env`, `service`, `status`, `owner`, `category`, `due_before`, `limit`, `offset`

### Tool: `backlog_tool`

```json
{
  "action": "list|get|dashboard|create|upsert|set_status|add_comment|close|auto_generate_weekly|cleanup",
  "env": "prod",
  "id": "bl_abc...",
  "service": "gateway",
  "status": "open",
  "item": { ... },
  "message": "...",
  "actor": "cto"
}
```

### RBAC

| Entitlement              | Roles            | Actions |
|--------------------------|------------------|---------|
| `tools.backlog.read`     | cto, oncall, interface | list, get, dashboard |
| `tools.backlog.write`    | cto, oncall      | create, upsert, set_status, add_comment, close |
| `tools.backlog.admin`    | cto only         | auto_generate_weekly, cleanup |

---

## Storage Backends

| Backend   | Env var                | Notes |
|-----------|------------------------|-------|
| `auto`    | `BACKLOG_BACKEND=auto` | Postgres → JSONL fallback (default) |
| `postgres`| `BACKLOG_BACKEND=postgres` | Primary (requires migration) |
| `jsonl`   | `BACKLOG_BACKEND=jsonl`    | Filesystem append-only (MVP) |
| `memory`  | `BACKLOG_BACKEND=memory`   | Tests only |
| `null`    | `BACKLOG_BACKEND=null`     | No-op |

Files (JSONL): `ops/backlog/items.jsonl`, `ops/backlog/events.jsonl`

Postgres: run `ops/scripts/migrate_backlog_postgres.py` first.

---

## Scheduled Jobs

| Job                        | Schedule           | Description |
|----------------------------|--------------------|-------------|
| `weekly_backlog_generate`  | Mon 06:20 UTC      | Generate items from latest platform digest |
| `daily_backlog_cleanup`    | Daily 03:40 UTC    | Remove done/canceled items older than retention_days |

---

## Examples

### Manual create via tool

```json
{
  "action": "create",
  "env": "prod",
  "item": {
    "service": "gateway",
    "category": "security",
    "title": "[SEC] Patch CVE-2026-xxxx in gateway",
    "priority": "P0",
    "due_date": "2026-03-01",
    "owner": "cto",
    "source": "manual",
    "dedupe_key": "manual:2026-W08:prod:gateway:security"
  }
}
```

### Close an item

```json
{
  "action": "close",
  "id": "bl_abc123456789",
  "status": "done",
  "message": "Architecture review completed — no rework needed."
}
```

### Run weekly auto-generation

```bash
# HTTP
POST /v1/backlog/generate/weekly?env=prod

# Tool
{ "action": "auto_generate_weekly", "env": "prod" }
```
