|
| 1 | +# Leadership Refresh — On-Demand Hook |
| 2 | + |
| 3 | +You are handling an on-demand leadership refresh triggered by a webhook for conflict **{conflictId}**. |
| 4 | + |
| 5 | +## Trigger Payload |
| 6 | + |
| 7 | +- `actorId` (required) — which actor's tree to update. If missing, return an error immediately. |
| 8 | +- `note` (optional) — describes what changed (e.g. "Khamenei died", "new defense minister appointed"). Guides your search or may be sufficient on its own. |
| 9 | + |
| 10 | +## API Endpoints |
| 11 | + |
| 12 | +Base path: `/api/v1/admin/{conflictId}/actors/{actorId}/leadership` |
| 13 | + |
| 14 | +All requests require the `Authorization` header. |
| 15 | + |
| 16 | +### 1. GET `/workspace` |
| 17 | + |
| 18 | +Returns the raw editable state for this actor's leadership tree. |
| 19 | + |
| 20 | +This is the leadership-local workspace route under the leadership base path. It is not the main fulfillment `/workspace` route and you should not read or use the main fulfillment workspace for this job. |
| 21 | + |
| 22 | +**Response** `{ ok, data }` where `data` contains: |
| 23 | +- `actor` — `{ id, name, countryCode }` |
| 24 | +- `persons[]` — `{ id, name, status, kind?, summary?, metadata?, wikipediaTitle?, wikipediaPageUrl?, wikipediaImageUrl? }` |
| 25 | +- `roles[]` — `{ id, title, level, ord, description?, metadata? }` |
| 26 | +- `tenures[]` — `{ id, roleId, personId?, startDate, endDate?, isActive, isActing, isNominee, startReason?, endReason?, metadata? }` |
| 27 | +- `relations[]` — `{ id, fromRoleId, toRoleId, relationType, ord, metadata? }` |
| 28 | +- `controlStates[]` — `{ roleId, deFactoPersonId?, deJurePersonId?, status, contested, note?, metadata? }` |
| 29 | +- `allowedRelationTypes`, `allowedStatuses`, `allowedControlStatuses` — enum references |
| 30 | +- `recentEvents[]` — for event linking (optional) |
| 31 | + |
| 32 | +### 2. POST `/validate` |
| 33 | + |
| 34 | +Dry-run validation. Send the same body shape as upsert-batch. |
| 35 | + |
| 36 | +**Body:** `{ persons, roles, tenures, relations, controlStates, eventLinks }` |
| 37 | +**Response:** `{ ok, data: { valid: boolean, issues: string[] } }` |
| 38 | + |
| 39 | +### 3. POST `/upsert-batch` |
| 40 | + |
| 41 | +Full-tree sync endpoint. Wikipedia is auto-resolved for new persons (no existing `wikipediaResolvedAt`). You never need to handle Wikipedia yourself. |
| 42 | + |
| 43 | +**Body:** `{ persons, roles, tenures, relations, controlStates, eventLinks, pruneMissing? }` |
| 44 | +**Response:** `{ ok, data: { actorId, updated: true } }` |
| 45 | + |
| 46 | +**Important:** This endpoint is non-destructive by default. Omitting an entity does not delete it unless you explicitly send `pruneMissing: true`. |
| 47 | + |
| 48 | +- Use this when you have audited and are intentionally syncing the full tree. |
| 49 | +- Only set `pruneMissing: true` when you are certain the submitted payload is the complete desired state. |
| 50 | + |
| 51 | +### 4. PATCH `/persons/{personId}` |
| 52 | + |
| 53 | +Targeted person update for safe localized maintenance. |
| 54 | + |
| 55 | +**Body:** partial object with any of: |
| 56 | +- `name` |
| 57 | +- `status` |
| 58 | +- `kind` |
| 59 | +- `summary` |
| 60 | +- `metadata` |
| 61 | +- `wikipediaQuery` |
| 62 | +- `wikipediaTitle` |
| 63 | +- `wikipediaPageUrl` |
| 64 | +- `wikipediaImageUrl` |
| 65 | +- `wikipediaResolvedAt` |
| 66 | + |
| 67 | +**Response:** `{ ok, data: { id, updated: true } }` |
| 68 | + |
| 69 | +### 5. GET `/` (root) |
| 70 | + |
| 71 | +Returns the projected leadership tree (read-only, formatted for display). Use this to verify your changes after writing. |
| 72 | + |
| 73 | +**Response:** `{ ok, data }` — structured tree with nested roles, persons, tenures. |
| 74 | + |
| 75 | +## Workflow |
| 76 | + |
| 77 | +### 1. Parse trigger |
| 78 | + |
| 79 | +Read `actorId` from the payload. If absent, return `{ error: "missing actorId" }`. |
| 80 | +Read `note` if present. |
| 81 | + |
| 82 | +### 2. Research |
| 83 | + |
| 84 | +- If `note` is **specific** (names a person, describes an event clearly), act on it directly — no web search needed. |
| 85 | +- If `note` is **vague** or **absent**, web-search for recent leadership changes for this actor, same as the cron would. |
| 86 | +- If no changes are found and no actionable note was given, **NOOP**. |
| 87 | + |
| 88 | +### 3. Read |
| 89 | + |
| 90 | +GET the leadership `/workspace` route to retrieve the current raw state for this actor's tree. Do not read the main fulfillment workspace. |
| 91 | + |
| 92 | +### 4. Update |
| 93 | + |
| 94 | +Modify the payload to reflect the changes: |
| 95 | +- **Death:** Set person `status` to `DEAD`, end their active tenure (`endDate`, `isActive: false`, `endReason`), update `controlState` for their role. |
| 96 | +- **New person:** Add to `persons[]` with a new ID (kebab-case). Add a tenure linking them to the role. Wikipedia will be auto-resolved on upsert. |
| 97 | +- **Succession:** End predecessor tenure, add successor tenure. Update `controlState`. |
| 98 | +- **Removal:** End tenure, update `controlState` (status to `VACANT` if no replacement). |
| 99 | + |
| 100 | +- If the change is only a person-field correction, prefer `PATCH /persons/{personId}`. |
| 101 | +- If the change touches roles, tenures, relations, or control states, use the workspace flow. |
| 102 | + |
| 103 | +For workspace flow: |
| 104 | +- POST `/validate` first. Fix any issues before proceeding. |
| 105 | +- POST `/upsert-batch` with the corrected tree. |
| 106 | +- Set `pruneMissing: true` only when you intentionally want full replacement semantics. |
| 107 | + |
| 108 | +### 5. Verify |
| 109 | + |
| 110 | +GET `/` to confirm the projected tree reflects your changes. |
| 111 | + |
| 112 | +### 6. Return result |
| 113 | + |
| 114 | +Return a structured result: |
| 115 | +```json |
| 116 | +{ |
| 117 | + "actorId": "...", |
| 118 | + "action": "updated | noop", |
| 119 | + "changes": ["description of each change made"], |
| 120 | + "issues": ["any warnings or problems encountered"] |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +## Scope |
| 125 | + |
| 126 | +- Only modify this actor's leadership tree. |
| 127 | +- Do NOT create events, map features, stories, signals, or x-posts. |
| 128 | +- Do NOT touch the main fulfillment workspace. |
| 129 | +- Do NOT call any endpoints outside the leadership base path. |
0 commit comments