bruno: regenerate collection from canonical docs + permanent docs↔Bruno parity gate

WO4. The old collection was the drifted artifact AGENTS.md warned about:
bodies pre-truncated at their first {{var}}, routes renamed server-side
but not here (/api/notes/:media_id, /api/book-matching/search,
/api/collections/:id/rules/test), an orphan universal-progress suite for
routes that no longer exist.

scripts/gen_bruno_from_docs.py emits one OpenCollection request per
frontmatter'd endpoint doc (191 requests): gate-verified example-request
JSON as body, {{env}} params per the environment file, device-token
routes get an explicit Bearer {{device_token}} header, login/register/
refresh keep the token-capture after-response scripts that chain the
collection. Old 1:1 mirrors deleted (128); hand-authored NewDevDBSetup,
environments, and edge-case suites kept. 30 scenario files with
truncated bodies (the documented corruption) deleted — they could not
run.

internal/docs/drift_test.go gains TestBrunoMirrorsDocs: every
frontmatter'd doc must have a Bruno request with matching method+URL
shape — the collection can never silently drift from the docs again.
opencollection.yml README block replaced with the real run instructions.

make test green (9 pkgs, parity test included in internal/docs).
This commit is contained in:
John O'Keefe committed 2026-10-24 18:35:51 -04:00
1 parent 6fc501b35a
commit acd82fff0a
353 files changed
+5165 -5293

No files matched your search

-22
View File
@@ -1,22 +0,0 @@
info:
name: Delete Conflict
type: http
seq: 4
http:
method: DELETE
url: '{{base_url}}/api/conflicts/{{conflict_id}}'
auth: inherit
body:
type: none
headers:
- key: Authorization
value: Bearer {{token
docs: |-
## Delete Conflict
Permanently deletes a specific conflict record from the system.
**Method:** DELETE
**Endpoint:** /api/conflicts/{conflict_id
-37
View File
@@ -1,37 +0,0 @@
info:
name: Dismiss All Resolved Conflicts
type: http
seq: 5
http:
method: POST
url: '{{base_url}}/api/conflicts/dismiss-all'
auth: inherit
body:
type: none
headers:
- key: Authorization
value: Bearer {{token
docs: |-
## Dismiss All Resolved Conflicts
Deletes all resolved conflicts for the authenticated user, cleaning up the conflict list.
**Method:** POST
**Endpoint:** /api/conflicts/dismiss-all
**Authentication:** Bearer token
**Response:**
- `deleted` (number): Number of conflict records that were deleted
**Status Codes:**
- 200: Success - conflicts deleted
- 401: Unauthorized
- 500: Internal server error
**Example Response:**
```json
{
"deleted": 5
-22
View File
@@ -1,22 +0,0 @@
info:
name: Get Conflict Details
type: http
seq: 2
http:
method: GET
url: '{{base_url}}/api/conflicts/{{conflict_id}}'
auth: inherit
body:
type: none
headers:
- key: Authorization
value: Bearer {{token
docs: |-
## Get Conflict Details
Retrieves detailed information about a specific conflict, including side-by-side comparison of conflicting data from all sources.
**Method:** GET
**Endpoint:** /api/conflicts/{conflict_id
-74
View File
@@ -1,74 +0,0 @@
info:
name: List Conflicts
type: http
seq: 1
http:
method: GET
url: '{{base_url}}/api/conflicts?status=unresolved'
auth: inherit
body:
type: none
headers:
- key: Authorization
value: Bearer {{token
docs: |-
## List Conflicts
Lists all sync conflicts for the authenticated user with optional filtering.
**Method:** GET
**Endpoint:** /api/conflicts
**Authentication:** Bearer token
**Query Parameters:**
- `status` (string, optional): Filter by resolution status
- `unresolved`: Only unresolved conflicts (default)
- `user_resolved`: Conflicts resolved by user
- `auto_resolved`: Automatically resolved conflicts
- `all`: All conflicts regardless of status
- `type` (string, optional): Filter by conflict type
- `progress`: Reading progress conflicts
- `note`: Bookmark/note conflicts
- `highlight`: Highlight conflicts
**Response:**
- `conflicts` (array): Array of conflict objects
- `total` (number): Total number of conflicts matching filters
- `unresolved` (number): Number of unresolved conflicts
**Each Conflict Object:**
- `id` (string): Conflict UUID
- `media_item_id` (string): Associated book UUID
- `media_item_title` (string): Book title
- `conflict_type` (string): Type of conflict (progress, note, highlight)
- `conflict_data` (object): Side-by-side comparison of conflicting data
- `koreader`: Data from KOReader device
- `kobo`: Data from Kobo device
- `web`: Data from web interface
- `resolution_status` (string): Current status (unresolved, user_resolved, auto_resolved)
- `created_at` (string): ISO 8601 timestamp when conflict was detected
**Status Codes:**
- 200: Success
- 401: Unauthorized
- 500: Internal server error
**Example Request:**
```
GET /api/conflicts?status=unresolved&type=progress
```
**Example Response:**
```json
{
"conflicts": [
{
"id": "conflict-uuid",
"media_item_id": "book-uuid",
"media_item_title": "Foundation",
"conflict_type": "progress",
"conflict_data": {
"koreader": { "percentage": 0.65, "epubcfi": "..."
-15
View File
@@ -1,15 +0,0 @@
info:
name: Resolve Conflict
type: http
seq: 3
http:
method: POST
url: '{{base_url}}/api/conflicts/{{conflict_id}}/resolve'
auth: inherit
body:
type: json
data: "{\n \"winner\": \"koreader\",\n \"manual_data\": null,\n \"apply_to_all_future_conflicts\": false,\n \"reason\": \"User chose more recent progress\""
headers:
- key: Authorization
value: Bearer {{token
docs: "## Resolve Conflict\n\n Resolves a sync conflict by choosing which source to use for the conflicting data.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/{conflict_id"
@@ -0,0 +1,30 @@
info:
name: Bulk Dismiss Conflicts
type: http
seq: 1
http:
method: POST
url: '{{base_url}}/api/conflicts/bulk-dismiss'
auth: inherit
body:
type: json
data: |-
{
"conflict_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"660e8400-e29b-41d4-a716-446655440001"
]
}
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
docs: |-
# Bulk Dismiss Conflicts
`POST /api/conflicts/bulk-dismiss` — auth: jwt.
Canonical reference: `docs/developer/api/conflicts/bulk_dismiss_conflicts.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
@@ -0,0 +1,31 @@
info:
name: Bulk Resolve Conflicts
type: http
seq: 2
http:
method: POST
url: '{{base_url}}/api/conflicts/bulk-resolve'
auth: inherit
body:
type: json
data: |-
{
"conflict_ids": [
"550e8400-e29b-41d4-a716-446655440000",
"660e8400-e29b-41d4-a716-446655440001"
],
"strategy": "highest_progress"
}
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
docs: |-
# Bulk Resolve Conflicts
`POST /api/conflicts/bulk-resolve` — auth: jwt.
Canonical reference: `docs/developer/api/conflicts/bulk_resolve_conflicts.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
+21
View File
@@ -0,0 +1,21 @@
info:
name: Delete Conflict
type: http
seq: 3
http:
method: DELETE
url: '{{base_url}}/api/conflicts/{{media_item_id}}'
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
docs: |-
# Delete Conflict
`DELETE /api/conflicts/:id` — auth: jwt.
Canonical reference: `docs/developer/api/conflicts/delete_conflict.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
+21
View File
@@ -0,0 +1,21 @@
info:
name: Dismiss All Resolved
type: http
seq: 4
http:
method: POST
url: '{{base_url}}/api/conflicts/dismiss-all'
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
docs: |-
# Dismiss All Resolved
`POST /api/conflicts/dismiss-all` — auth: jwt.
Canonical reference: `docs/developer/api/conflicts/dismiss_all_resolved.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
+21
View File
@@ -0,0 +1,21 @@
info:
name: Get Conflict Details
type: http
seq: 5
http:
method: GET
url: '{{base_url}}/api/conflicts/{{media_item_id}}'
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
docs: |-
# Get Conflict Details
`GET /api/conflicts/:id` — auth: jwt.
Canonical reference: `docs/developer/api/conflicts/get_conflict.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
+21
View File
@@ -0,0 +1,21 @@
info:
name: List Conflicts
type: http
seq: 6
http:
method: GET
url: '{{base_url}}/api/conflicts'
auth: inherit
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
docs: |-
# List Conflicts
`GET /api/conflicts` — auth: jwt.
Canonical reference: `docs/developer/api/conflicts/list_conflicts.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
+27
View File
@@ -0,0 +1,27 @@
info:
name: Resolve Conflict
type: http
seq: 7
http:
method: POST
url: '{{base_url}}/api/conflicts/{{media_item_id}}/resolve'
auth: inherit
body:
type: json
data: |-
{
"winner": "kobo"
}
settings:
encodeUrl: true
timeout: 0
followRedirects: true
maxRedirects: 5
docs: |-
# Resolve Conflict
`POST /api/conflicts/:id/resolve` — auth: jwt.
Canonical reference: `docs/developer/api/conflicts/resolve_conflict.md` (docs site → Developer → API). This request is GENERATED from that doc; edit the doc, not this file.
@@ -1,17 +0,0 @@
info:
name: Bulk Dismiss Conflicts
type: http
seq: 1
http:
method: POST
url: '{{base_url}}/api/conflicts/bulk-dismiss'
auth: inherit
body:
type: json
data: "{\n \"conflict_ids\": [\n \"{{conflictId1"
headers:
- key: Content-Type
value: application/json
- key: Authorization
value: Bearer {{authToken
docs: "## Bulk Dismiss Conflicts\n\n Dismisses multiple sync conflicts without resolving them. This removes them from the conflict list while leaving the data unchanged.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/bulk-dismiss\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `conflict_ids` (array): Array of conflict UUIDs to dismiss\n\n **Response:**\n - `results` (array): Results for each dismissal\n - `total` (number): Total number of conflicts processed\n - `success` (number): Number of successfully dismissed conflicts\n - `failed` (number): Number of failed dismissals\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid request data\n - 401: Unauthorized\n - 403: Forbidden\n - 404: One or more conflicts not found\n - 500: Internal server error\n\n **Example:**\n ```json\n {\n \"conflict_ids\": [\"uuid-1\", \"uuid-2\"]"
@@ -1,18 +0,0 @@
info:
name: Bulk Resolve Conflicts
type: http
seq: 1
http:
method: POST
url: '{{base_url}}/api/conflicts/bulk-resolve'
auth: inherit
body:
type: json
data: "{\n \"conflict_ids\": [\n \"{{conflictId1"
headers:
- key: Content-Type
value: application/json
- key: Authorization
value: Bearer {{authToken
docs: "## Bulk Resolve Conflicts\n\n Resolves multiple sync conflicts in a single request using a specified resolution strategy.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/bulk-resolve\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `conflict_ids` (array): Array of conflict UUIDs to resolve\n - `strategy` (string): Resolution strategy\n - `most_recent`: Use the most recently updated progress\n - `highest_progress`: Use the reading progress with the highest percent read\n - `server`: Always prefer server-side data\n - `device`: Always prefer device-side data\n\n **Response:**\n - `results` (array): Results for each conflict resolution\n - `total` (number): Total number of conflicts processed\n - `success` (number): Number of successfully resolved conflicts\n - `failed` (number): Number of failed resolutions\n\n **Status Codes:**\n - 200: Success (with partial results if some failed)\n - 400: Invalid request data\n - 401: Unauthorized\n\
\ - 403: Forbidden\n - 404: One or more conflicts not found\n - 500: Internal server error\n\n **Example:**\n ```json\n {\n \"conflict_ids\": [\"uuid-1\", \"uuid-2\", \"uuid-3\"],\n \"strategy\": \"most_recent\""
@@ -1,18 +0,0 @@
info:
name: Bulk Resolve with Highest Progress Strategy
type: http
seq: 1
http:
method: POST
url: '{{base_url}}/api/conflicts/bulk-resolve'
auth: inherit
body:
type: json
data: "{\n \"conflict_ids\": [\n \"{{conflictId1"
headers:
- key: Content-Type
value: application/json
- key: Authorization
value: Bearer {{authToken
docs: "## Bulk Resolve with Highest Progress Strategy\n\n Resolves multiple sync conflicts using the \"highest_progress\" strategy, which keeps the reading progress with the highest percentage read.\n\n **Method:** POST\n\n **Endpoint:** /api/conflicts/bulk-resolve\n\n **Authentication:** Bearer token\n\n **Request Body:**\n - `conflict_ids` (array): Array of conflict UUIDs to resolve\n - `strategy` (string): Must be \"highest_progress\"\n\n **Response:**\n - `results` (array): Results for each conflict resolution\n - `total` (number): Total number of conflicts processed\n - `success` (number): Number of successfully resolved conflicts\n - `failed` (number): Number of failed resolutions\n\n **Status Codes:**\n - 200: Success\n - 400: Invalid request data\n - 401: Unauthorized\n - 403: Forbidden\n - 500: Internal server error\n\n **Note:** The highest progress strategy is ideal when you want to preserve the most reading progress across devices. Use this when you've been\
\ reading on multiple devices and want to keep the furthest position."