---
name: maintenance-project-management
description: Use when setting up or managing a maintenance/technical support project in Plane — creating project structure, triaging tickets, logging service records, running maintenance cycles, and connecting Plane to Rex workflows.
triggers:
  - Project moves from active development to maintenance stage
  - Client needs ongoing technical support tracking
  - Setting up a new maintenance project in Plane
  - Triaging incoming support tickets from Lark/Feishu
  - Generating monthly service reports
---

# Maintenance Project Management in Plane

## 1. Project Setup

### Create a Dedicated Maintenance Project
Never mix active development with maintenance. Create a separate project:
```
[ClientName] — Maintenance
e.g. [Client] — Maintenance
     [Company] — Maintenance
     [Product] — Maintenance
```

### States (in order)
```
New → Triaged → In Progress → Pending Client → Pending 3rd Party → Resolved → Closed
```
- Never skip Resolved → Closed: client must confirm before closing
- Service Logs are created directly as Closed (they are records, not tasks)

### Labels to Create
| Label | Use |
|-------|-----|
| `P1-Critical` | System down, data loss risk |
| `P2-High` | Major feature broken, no workaround |
| `P3-Medium` | Degraded, workaround exists |
| `P4-Low` | Cosmetic, minor inconvenience |
| `Service Log` | Routine operational record |
| `Change Request` | Client-requested enhancement |
| `Recurring` | Same root cause seen 2+ times |

### Modules to Create
| Module | Purpose |
|--------|---------|
| Incidents | Customer-reported bugs and outages |
| Service Logs | Proactive operational records |
| Change Requests | Client-requested changes |
| Runbooks | SOPs (use Plane Pages) |

---

## 2. Ticket Format

### Required Fields
- **Title**: `[Site/Client] — [Brief description]`
  e.g. `TC42 — RFID Reader offline after power cycle`
- **Priority**: P1–P4 label
- **Assignee**: Named owner
- **Due Date**: Based on SLA (see Section 4)
- **Description**:
  ```
  What happened:
  When:
  Impact:
  Steps to reproduce (if applicable):
  ```
- **Resolution fields** (fill on close):
  ```
  Root Cause:
  Fix Applied:
  Prevention:
  ```

### Service Log Format
Label: `Service Log` | State: `Closed` (pre-closed)
```
Title:    [YYYY-MM-DD] [Site] — [Check type]
Example:  [2026-06-09] TC42 — Monthly Health Check

Notes:
  - System status: nominal / degraded / critical
  - CPU / disk / memory summary
  - Services checked: all running / issues found
  - Firmware/software versions current: yes / no
  - Action taken (if any):
  - Next scheduled check:
```

### Running Service Log (Plane Pages)
For continuous per-site logs, use a Plane Page:
```
Page: "[Site] Service Log [Year]"
  - YYYY-MM-DD: [check type] — [result] — [action]
  - YYYY-MM-DD: [check type] — [result] — [action]
```

---

## 3. Maintenance Cycle

### Weekly (5 min)
- [ ] Check all open P1/P2 tickets — anything overdue?
- [ ] Move resolved tickets to Resolved state (pending client confirm)
- [ ] Log any health checks performed this week

### Monthly
- [ ] Create Monthly Service Report (Plane Page)
  - Count by label: P1/P2/P3/P4, Service Log, Change Request
  - List Recurring-tagged tickets → flag for product signal to Iris / founders
  - Summarize any SLA breaches
- [ ] Archive completed service log entries

### Quarterly
- [ ] Review all `Recurring` tickets
  - 3+ same root cause → create Change Request
  - Route as structured product feedback to Iris / founders
- [ ] Update Runbook pages with new procedures discovered
- [ ] Review SLA performance — report to Iris

---

## 4. SLA Targets

| Priority | First Response | Resolution Target |
|----------|---------------|-------------------|
| P1 — System Down | 1 hour | 4 hours |
| P2 — Major Degraded | 4 hours | 24 hours |
| P3 — Minor Issue | 24 hours | 72 hours |
| P4 — Low / Service Log | 48 hours | 1 week |

Track via Plane due date. Flag overdue P1/P2 immediately to the founder.

---

## 5. Rex Integration Workflow

### Incoming ticket from Lark/Feishu
1. Client reports issue via Lark DM or group
2. Rex triages: assign P1–P4, identify client/site
3. Rex creates Plane ticket with correct label, assignee, due date
4. Rex acknowledges client in Lark with ticket reference
5. Rex updates ticket state as work progresses
6. On resolution: Rex updates Root Cause + Fix Applied fields, moves to Resolved
7. Rex notifies client in Lark, asks for confirmation
8. Client confirms → Rex closes ticket

### Pattern Detection (Monthly)
- Pull all `Recurring` tickets from the month
- If 3+ tickets share same root cause → draft structured feedback for Iris / founders
- Format: what broke, how often, which clients affected, proposed fix

### Plane Access
- Plane URL: http://{{OPERATOR_TAILNET_IP}}:8090 (Tailscale) / http://34.132.28.199:8090 (public)
- API base: `http://localhost:8090/api/v1/workspaces/{slug}/projects/` for project-scoped endpoints
- License/instance endpoints: `/api/instances/` (NOT `/api/v1/instances/`)
- Auth: `x-api-key: {token}` header — API token stored in memory and `clients/client-site` GBrain page
- Workspace slug: `dataxquad`

### Plane API Setup Sequence (first-time project)
When creating a maintenance/support/integration project from scratch via API:
1. **Create project** → `POST /api/v1/workspaces/{slug}/projects/`; include a concise project brief in `description`
2. **Enable modules** → `PATCH /api/v1/workspaces/{slug}/projects/{id}/` with `{"module_view": true}` — modules are disabled by default
3. **Rename/add states** → `PATCH .../states/{id}/` for existing, `POST .../states/` for new ones
4. **Create labels** → `POST .../labels/`
5. **Create modules** → `POST .../modules/`; for new integration work, start with one MVP module before splitting scope
6. **Create issues** → `POST .../issues/` with state ID, label IDs (array), priority
7. **Link issues to modules** → `POST .../modules/{module_id}/module-issues/` with `{"issues": [id1, id2]}` — NOT `.../modules/{id}/issues/`
8. **Create project Page(s)** for human-readable brief / runbook / client questions
9. **Create project View(s)** for MVP module, critical path, and client clarifications
10. **Verify** by re-reading project, modules, issues, linked module issues, pages, and views before reporting success

### Plane Pages and Views
After creating a project with more than a few work items, add at least one Page and useful Views so the founder/client can navigate without reading every issue.

### Review-gated work item updates
When the founder asks to move work items to a stage for review, treat that as a review gate — not completion.
- Move only the items the founder actually requested. If the request says "the task you done yesterday" but the project has many planned items, identify the completed/reviewable subset rather than moving every issue.
- Preserve the workflow spread: items that have not started should remain in `Backlog` or `Todo`; do not collapse all planned work into `In Progress` just because descriptions were updated.
- Use a logical split when the founder asks for "some in Todo / some in Backlog": immediate next execution steps → `Todo`; later/blocked/follow-up work → `Backlog`.
- Do **not** move items to `Done` / `Closed` until the founder confirms review is complete or explicitly names the sequence numbers/items that can be done.
- In each issue description, include: `Task details`, `Test needed`, and `Test result`.
- Mark test results honestly: `Passed` only when real execution/field evidence exists or the founder explicitly approves a planning/setup item; otherwise use `Pending / not passed yet`, `Partial pass`, or `Blocked` with the reason.
- Verify after patching by re-reading every issue and checking both state and required description sections.

📎 See `references/plane-issue-review-gate-and-bulk-update.md` for the API pattern and exact description template.

### Project-only customer guest invites
When the founder asks to add customer emails to one Plane project, invite them as project **Guest** (`role=5`) unless he specifies another role.
- Use the project invitations endpoint first; verify pending invites or accepted project members afterward.
- Do not grant broad workspace/project access beyond the named project.
- If email delivery is unavailable or uncertain, create/verify the invite records and return manual invitation links for the founder to forward.
- Existing Plane users can be directly activated as Guest for the target project, but still verify no unrelated project memberships were created.

📎 See `references/plane-project-guest-invites.md` for the endpoint, Django-shell fallback, manual link format, and verification checklist.

Recommended Page:
- `Project Brief + Client Clarifications` — objective, scope, current machine/system audit, delivery plan, open questions, immediate next step.

Recommended Views:
- `MVP Module Work Items` — filter by `module: [<module_uuid>]`
- `High Priority / Critical Path` — filter by `priority: ["high"]`
- `Client Clarifications` — filter by `name: "Clarify"` or use labels when labels exist

Endpoint quirk in this Plane deployment:
- Project/issues/modules use `/api/v1/...` and accept `x-api-key`.
- Pages/views are under `/api/workspaces/{slug}/projects/{project_id}/pages/` and `/api/workspaces/{slug}/projects/{project_id}/views/`.
- If `/api/...` returns `401 Authentication credentials were not provided` while `/api/v1/...` works, create/update Page and IssueView via Django shell (`Page`, `ProjectPage`, `IssueView`, `issue_filters`) instead of retrying API headers.

**Rate-limit-safe shell pattern** (add `sleep 1` between every call, `sleep 2` between projects):
```bash
# Enable modules
curl -s -X PATCH "$BASE/projects/$PID/" -H "x-api-key: $TK" -H "Content-Type: application/json" -d '{"module_view":true}'
sleep 2
# Per state/label/module:
curl -s -X POST "$BASE/projects/$PID/states/" -H "x-api-key: $TK" -H "Content-Type: application/json" -d "$PAYLOAD"
sleep 1
```

📎 See `scripts/setup-maintenance-project.sh` for a reusable setup script for the standard SSSS/[Client] project shape.

---

## 6. Escalation Rules

| Situation | Action |
|-----------|--------|
| P1 unresolved > 2 hours | Escalate to the operator/the founder immediately |
| P2 unresolved > 12 hours | Notify the founder |
| Client unresponsive > 7 days post-resolution | Proactive check-in |
| 3+ same root cause tickets | Route to Iris / founders as product signal |
| Commercial / contract implication | Escalate to the founder, do not respond to client |

---

## Pitfalls

- Do NOT close tickets without client confirmation — always pass through Resolved first
- Do NOT create maintenance tickets inside active development projects — context and SLAs are different
- Service Logs are records, not tasks — create them pre-Closed with label `Service Log`
- When using Plane API, check for workspace slug + project ID before making calls — do not hardcode
- `Recurring` label is a signal, not a status — a ticket can be both P2 and Recurring
- **Module endpoint pitfall:** linking issues to modules requires `POST .../module-issues/` (not `.../issues/`). Using the wrong path returns 404.
- **Modules disabled by default:** must PATCH project with `{"module_view": true}` before creating modules.
- **State group matters:** when creating custom states, `group` must be one of `backlog`, `unstarted`, `started`, `completed`, `cancelled` — use `unstarted` for waiting states (Pending Client, Pending 3rd Party).
- **GBrain is the canonical home for site credentials and architecture** — Plane tickets reference it but don't duplicate it. Credentials, IPs, service names, SSH patterns go in `clients/{site}-site` GBrain page.
- ❌ **Project names cannot contain special characters** — em-dash `—`, ampersand `&`, brackets etc. all fail with `non_field_errors: Project name cannot contain special characters`. Use plain ASCII: `BWS Maintenance and Support` not `BWS — Maintenance & Support`.
- ❌ **Plane CE rate-limits API calls (error_code 5900)** — when creating states + labels + modules across multiple projects in rapid succession, `RATE_LIMIT_EXCEEDED` will hit. Add `sleep 1` between each individual call and `sleep 2` between projects. Batching 5 projects × 14 calls without sleeps will fail the last project(s) every time.
- ✅ **Rate limit recovery**: wait ~35 seconds after a 5900 error before retrying — shorter waits will still get rate-limited.
