@bongos/core 1.19.293 → 1.19.295

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.bongos-core.json CHANGED
@@ -2,22 +2,22 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.293",
6
- "core_contract": "1.19.293",
7
- "source_commit": "d37d65e03910c166c3a3af9827ffa2a2d8f8a204",
5
+ "core_version": "1.19.295",
6
+ "core_contract": "1.19.295",
7
+ "source_commit": "a9c6feaf0d724e69e2c74fae890f66887a1f6052",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-22T16:56:18.512Z",
9
+ "built_at": "2026-08-22T18:22:26.661Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 321,
13
13
  "agent_docs_stubbed": 22,
14
- "functional_verbatim": 1393,
14
+ "functional_verbatim": 1397,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1736,
20
- "tree_sha256": "a7ed89984671394923e64db2b93e69efd81ef112f856a58f58009bf4bc0c4b1d",
19
+ "file_count": 1740,
20
+ "tree_sha256": "0d0f553d620644b95da343b98d6a1bb50e5e934ea015e43de5f7630c1d505842",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -347,12 +347,12 @@
347
347
  {
348
348
  "path": "clients/bongos-client/README.md",
349
349
  "mode": "0000644",
350
- "sha256": "aaf0bb7dae10d271e9999093935fef5ae8d85c3a2e3d5b4c52e0c11fed8c581e"
350
+ "sha256": "c5008815c70e20b7bbc49668082bd6886f1ac406d948a4d27f5f9fd4ff70188e"
351
351
  },
352
352
  {
353
353
  "path": "clients/bongos-client/bongos-client.global.js",
354
354
  "mode": "0000644",
355
- "sha256": "372a5584ddad52fb524815fc7b50d1aab9163e974aac6f2c64f479e4034914a1"
355
+ "sha256": "91cc8b99c22e82ef7bbcb93f4b1596e88d772bc50f1baafdd373c8fe0e9c8ce5"
356
356
  },
357
357
  {
358
358
  "path": "clients/bongos-client/examples/hello-world.mjs",
@@ -362,17 +362,17 @@
362
362
  {
363
363
  "path": "clients/bongos-client/index.cjs",
364
364
  "mode": "0000644",
365
- "sha256": "7faac7fc48e714c76c2b19feaaf9c730b2c9b10ab71ddc5697c21497d02f10ef"
365
+ "sha256": "a7cd2351a67f71cf889601718f792b299bbbf663c62d0fbdb43babc83a83b8ba"
366
366
  },
367
367
  {
368
368
  "path": "clients/bongos-client/index.d.ts",
369
369
  "mode": "0000644",
370
- "sha256": "7aba0fe1d782cb6308f57dc2e5f6fe0d5ffe0f537d4ffe846bdc7a03e0f8064d"
370
+ "sha256": "57dc78f1c14c72595b1efe6b269161732be2e84caa7823ebd82f045e26e4d76f"
371
371
  },
372
372
  {
373
373
  "path": "clients/bongos-client/index.mjs",
374
374
  "mode": "0000644",
375
- "sha256": "5f1d221bfbf8d329f883a5dc3e84414728c67201d66faceff04d48732e44b13c"
375
+ "sha256": "a077e8810feaa0efbfe25234b4153db206089deaed9071b45c2ae2f42f06fdaa"
376
376
  },
377
377
  {
378
378
  "path": "clients/bongos-client/package.json",
@@ -1412,12 +1412,12 @@
1412
1412
  {
1413
1413
  "path": "docs/api-reference.md",
1414
1414
  "mode": "0000644",
1415
- "sha256": "8fb83f272ef8810df2c517d9514495f44016da2e23cdd67937463f40c0d1605d"
1415
+ "sha256": "d501dff7e10bbb0cee28717f652c26fca962f8f756a98b3be6ff7f361a92d967"
1416
1416
  },
1417
1417
  {
1418
1418
  "path": "docs/api/openapi.json",
1419
1419
  "mode": "0000644",
1420
- "sha256": "f51be43a281c9609980fca57d2fa11cafb460a414e506174715d53c08cef6965"
1420
+ "sha256": "ebef1d04e8bc3e299c24a6113b2bc0ec43a92a0e93ff60fc801087bbdbb08fab"
1421
1421
  },
1422
1422
  {
1423
1423
  "path": "docs/architecture.md",
@@ -1552,7 +1552,7 @@
1552
1552
  {
1553
1553
  "path": "docs/file-map.md",
1554
1554
  "mode": "0000644",
1555
- "sha256": "9762b7f69397c28e103cdb39a9987483e23bd800a827f0d83983b62ef962616b"
1555
+ "sha256": "a1c109f23cb4916b89bbc5ba522ebee6413bcb812569a30cbc1e0a7a00f8e9bc"
1556
1556
  },
1557
1557
  {
1558
1558
  "path": "docs/handoff-template.md",
@@ -2807,7 +2807,7 @@
2807
2807
  {
2808
2808
  "path": "modules/dev-box/app/src/vendor/bongos-client.cjs",
2809
2809
  "mode": "0000644",
2810
- "sha256": "7faac7fc48e714c76c2b19feaaf9c730b2c9b10ab71ddc5697c21497d02f10ef"
2810
+ "sha256": "a7cd2351a67f71cf889601718f792b299bbbf663c62d0fbdb43babc83a83b8ba"
2811
2811
  },
2812
2812
  {
2813
2813
  "path": "modules/dev-box/app/test/unit.mjs",
@@ -3742,7 +3742,7 @@
3742
3742
  {
3743
3743
  "path": "modules/ideas/routes/inbox.js",
3744
3744
  "mode": "0000644",
3745
- "sha256": "47c2a3beb6bed3b206ec36344950fe115189debf75a38cf202bfafaa5dbb2adb"
3745
+ "sha256": "8b832303a87a6caf9fd4f7779967576bb615bcb893640171beeef175c8783174"
3746
3746
  },
3747
3747
  {
3748
3748
  "path": "modules/ideas/routing.js",
@@ -4537,12 +4537,12 @@
4537
4537
  {
4538
4538
  "path": "package-lock.json",
4539
4539
  "mode": "0000644",
4540
- "sha256": "090cb33345196e3e3d42d1d6b81a591095b1f3c622becfff8e1eed4ce3f0577f"
4540
+ "sha256": "930aad90fce5c0ac0d66ec5e44c4948f3b704a790037b9b4856fe4a022e9a08f"
4541
4541
  },
4542
4542
  {
4543
4543
  "path": "package.json",
4544
4544
  "mode": "0000644",
4545
- "sha256": "78db3e19a56e9aa909f0fa6314315c2cec90439bb3cc82111b974937d05f3fb2"
4545
+ "sha256": "cbd88d46d805881d4eb8cb4f35b34b77bb1d0b22be90682bc8cd47699b445a58"
4546
4546
  },
4547
4547
  {
4548
4548
  "path": "public-docs/index.html",
@@ -5779,6 +5779,11 @@
5779
5779
  "mode": "0000644",
5780
5780
  "sha256": "9c3a382def8c074a37bad66d7e3deef41c7f2bb9406f02f107be0e9a7dc7bfb3"
5781
5781
  },
5782
+ {
5783
+ "path": "scripts/gds/spark.js",
5784
+ "mode": "0000755",
5785
+ "sha256": "27a98a84ad1682bc9d17286c675fb75d14e2ff529d7ce43f848f54a631ee99bc"
5786
+ },
5782
5787
  {
5783
5788
  "path": "scripts/gds/start.js",
5784
5789
  "mode": "0000644",
@@ -5789,6 +5794,11 @@
5789
5794
  "mode": "0000644",
5790
5795
  "sha256": "5cc69c5f3301912325aa48481e63141523ad75241f021e0e8f1a6dc592048bdd"
5791
5796
  },
5797
+ {
5798
+ "path": "scripts/gds/strand-watch.js",
5799
+ "mode": "0000644",
5800
+ "sha256": "abcf87286fa3b3be81c18e5927495f64c140322a83223c4dc673d8325c08121f"
5801
+ },
5792
5802
  {
5793
5803
  "path": "scripts/gds/task.js",
5794
5804
  "mode": "0000644",
@@ -6052,7 +6062,7 @@
6052
6062
  {
6053
6063
  "path": "src/bongos/route-rank-check.js",
6054
6064
  "mode": "0000644",
6055
- "sha256": "97b8345a1d891151c3bc134926124a0ac929db661e0cfb26c6760c8c6030bafc"
6065
+ "sha256": "f2d893e0de84433cc99411e59aba98c1ca392fa8d5bd24449e1e9200f06d9ff8"
6056
6066
  },
6057
6067
  {
6058
6068
  "path": "src/bongos/routes.js",
@@ -6187,7 +6197,7 @@
6187
6197
  {
6188
6198
  "path": "src/module-api.js",
6189
6199
  "mode": "0000644",
6190
- "sha256": "d9155f571379dfbb8e352671aad7a062e52027b225fe615d61a18c1452dde5a0"
6200
+ "sha256": "e7b52e29073dcbe62ed6c069753c22f9c7cf3a69cf8987bc2f91a7b4b9571d99"
6191
6201
  },
6192
6202
  {
6193
6203
  "path": "src/module-loader/catalog.js",
@@ -7504,6 +7514,11 @@
7504
7514
  "mode": "0000644",
7505
7515
  "sha256": "7bea2d388fad9a6b186c4712d6921a554f1d24c70dcae5760b381782149068e7"
7506
7516
  },
7517
+ {
7518
+ "path": "tests/idea_spark_mark.mjs",
7519
+ "mode": "0000644",
7520
+ "sha256": "fb5af0aa1b34116114a1c734c548cafaa52eefda57bd145c0008995bc69addee"
7521
+ },
7507
7522
  {
7508
7523
  "path": "tests/idea_task_lineage.mjs",
7509
7524
  "mode": "0000644",
@@ -8534,6 +8549,11 @@
8534
8549
  "mode": "0000644",
8535
8550
  "sha256": "2ddc5ada648e7eabb41d3ae0afd39f885f3840c8ec14483f83330e8eade11330"
8536
8551
  },
8552
+ {
8553
+ "path": "tests/strand_watch.mjs",
8554
+ "mode": "0000644",
8555
+ "sha256": "bba2bde5b7a4a25ccddff58fb2fde0ef84ef67224e63819232221a461234a30f"
8556
+ },
8537
8557
  {
8538
8558
  "path": "tests/streak.mjs",
8539
8559
  "mode": "0000644",
@@ -5,7 +5,7 @@ A **generated**, zero-dependency typed client for the Bongos API — produced fr
5
5
  by hand; it regenerates when the spec changes, so it can never drift from the routes.
6
6
 
7
7
  - API version: **v1** (served at `/api/bongos/v1`)
8
- - 294 operations across 47 resource groups
8
+ - 296 operations across 47 resource groups
9
9
 
10
10
  ## Install
11
11
 
@@ -409,6 +409,10 @@ function createClient(opts = {}) {
409
409
  postInboxIdRatify: (args) => request("POST", "/inbox/{id}/ratify", { hasBody: true }, args),
410
410
  // POST /inbox/{id}/resubmit — rank: any-builder — POST /inbox/:id/resubmit
411
411
  postInboxIdResubmit: (args) => request("POST", "/inbox/{id}/resubmit", { hasBody: true }, args),
412
+ // DELETE /inbox/{id}/spark — rank: any-builder — DELETE /inbox/:id/spark
413
+ deleteInboxIdSpark: (args) => request("DELETE", "/inbox/{id}/spark", { hasBody: false }, args),
414
+ // POST /inbox/{id}/spark — rank: any-builder — POST /inbox/:id/spark
415
+ postInboxIdSpark: (args) => request("POST", "/inbox/{id}/spark", { hasBody: true }, args),
412
416
  // GET /inbox/awaiting-nod — rank: metic+archon — GET /inbox/awaiting-nod
413
417
  getInboxAwaitingNod: (args) => request("GET", "/inbox/awaiting-nod", { hasBody: false }, args),
414
418
  },
@@ -408,6 +408,10 @@ function createClient(opts = {}) {
408
408
  postInboxIdRatify: (args) => request("POST", "/inbox/{id}/ratify", { hasBody: true }, args),
409
409
  // POST /inbox/{id}/resubmit — rank: any-builder — POST /inbox/:id/resubmit
410
410
  postInboxIdResubmit: (args) => request("POST", "/inbox/{id}/resubmit", { hasBody: true }, args),
411
+ // DELETE /inbox/{id}/spark — rank: any-builder — DELETE /inbox/:id/spark
412
+ deleteInboxIdSpark: (args) => request("DELETE", "/inbox/{id}/spark", { hasBody: false }, args),
413
+ // POST /inbox/{id}/spark — rank: any-builder — POST /inbox/:id/spark
414
+ postInboxIdSpark: (args) => request("POST", "/inbox/{id}/spark", { hasBody: true }, args),
411
415
  // GET /inbox/awaiting-nod — rank: metic+archon — GET /inbox/awaiting-nod
412
416
  getInboxAwaitingNod: (args) => request("GET", "/inbox/awaiting-nod", { hasBody: false }, args),
413
417
  },
@@ -9,6 +9,7 @@ export interface DeleteBoxSshKeyIdResponse { ok: boolean; deleted: unknown }
9
9
  export interface DeleteDependenciesResponse { ok: boolean }
10
10
  export interface DeleteGovernmentAssignmentsBuilderIdRankKeyResponse { ok: boolean; builder_id: unknown; rank_key: unknown; removed: unknown }
11
11
  export interface DeleteGovernmentRanksRankKeyPermissionsPermissionKeyResponse { ok: boolean; rank_key: unknown; permission_key: unknown; removed: unknown }
12
+ export interface DeleteInboxIdSparkResponse { idea: unknown; sparked: boolean; unchanged: boolean }
12
13
  export interface DeleteMemoryFileResponse { deleted: boolean; builder_id: unknown; path: unknown }
13
14
  export interface DeleteTasksIdCriteriaCriterionIdResponse { ok: boolean; removed: unknown }
14
15
  export interface DeleteTasksIdDependenciesDepIdResponse { ok: boolean; removed: unknown }
@@ -246,6 +247,7 @@ export interface PostGovernmentRanksRequest { rank_key: string; label: string; d
246
247
  export interface PostGovernmentRanksResponse { rank: unknown }
247
248
  export interface PostInboxIdRatifyRequest { notes_md?: string }
248
249
  export interface PostInboxIdResubmitRequest { field_values: Record<string, unknown>; template_key?: string }
250
+ export interface PostInboxIdSparkResponse { idea: unknown; sparked: boolean; unchanged: boolean }
249
251
  export interface PostInboxRequest { title: string; body_md?: string; kind?: string; suggested_version?: string; suggested_priority?: number; goal_id?: number; grade?: string; template_key?: string; field_values?: Record<string, unknown> }
250
252
  export interface PostLearningsRequest { title: string; body_md: string; tags?: string[]; related_files?: string[]; source?: string; source_ref?: string; task_id?: number }
251
253
  export interface PostLearningsResponse { skipped?: boolean; reason?: unknown; learning?: unknown }
@@ -669,6 +671,10 @@ export interface BongosClient {
669
671
  postInboxIdRatify(args?: RequestArgs & { body?: PostInboxIdRatifyRequest }): Promise<ApiResponse>;
670
672
  /** POST /inbox/{id}/resubmit — rank: any-builder */
671
673
  postInboxIdResubmit(args: RequestArgs & { body: PostInboxIdResubmitRequest }): Promise<ApiResponse>;
674
+ /** DELETE /inbox/{id}/spark — rank: any-builder */
675
+ deleteInboxIdSpark(args?: RequestArgs): Promise<DeleteInboxIdSparkResponse>;
676
+ /** POST /inbox/{id}/spark — rank: any-builder */
677
+ postInboxIdSpark(args?: RequestArgs): Promise<PostInboxIdSparkResponse>;
672
678
  /** GET /inbox/awaiting-nod — rank: metic+archon */
673
679
  getInboxAwaitingNod(args?: RequestArgs): Promise<GetInboxAwaitingNodResponse>;
674
680
  };
@@ -405,6 +405,10 @@ export function createClient(opts = {}) {
405
405
  postInboxIdRatify: (args) => request("POST", "/inbox/{id}/ratify", { hasBody: true }, args),
406
406
  // POST /inbox/{id}/resubmit — rank: any-builder — POST /inbox/:id/resubmit
407
407
  postInboxIdResubmit: (args) => request("POST", "/inbox/{id}/resubmit", { hasBody: true }, args),
408
+ // DELETE /inbox/{id}/spark — rank: any-builder — DELETE /inbox/:id/spark
409
+ deleteInboxIdSpark: (args) => request("DELETE", "/inbox/{id}/spark", { hasBody: false }, args),
410
+ // POST /inbox/{id}/spark — rank: any-builder — POST /inbox/:id/spark
411
+ postInboxIdSpark: (args) => request("POST", "/inbox/{id}/spark", { hasBody: true }, args),
408
412
  // GET /inbox/awaiting-nod — rank: metic+archon — GET /inbox/awaiting-nod
409
413
  getInboxAwaitingNod: (args) => request("GET", "/inbox/awaiting-nod", { hasBody: false }, args),
410
414
  },
@@ -6601,6 +6601,108 @@
6601
6601
  ]
6602
6602
  }
6603
6603
  },
6604
+ "/inbox/{id}/spark": {
6605
+ "post": {
6606
+ "operationId": "post_inbox_id_spark",
6607
+ "tags": [
6608
+ "inbox"
6609
+ ],
6610
+ "summary": "POST /inbox/:id/spark",
6611
+ "description": "Mark a Quick Idea as a Big Idea Spark — \"I have the spark but not the full picture, someone develop this\". AUTHOR-ONLY: the mark is the author's consent to have their idea developed, so only they can set it. A Full Idea is refused (409) and a non-open idea cannot be newly marked; re-marking is a 200 no-op.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
6612
+ "x-rank": "any-builder",
6613
+ "x-source": "modules/ideas/routes/inbox.js",
6614
+ "parameters": [
6615
+ {
6616
+ "name": "id",
6617
+ "in": "path",
6618
+ "required": true,
6619
+ "schema": {
6620
+ "type": "string"
6621
+ },
6622
+ "description": "Path parameter `id`."
6623
+ }
6624
+ ],
6625
+ "responses": {
6626
+ "200": {
6627
+ "description": "Success.",
6628
+ "content": {
6629
+ "application/json": {
6630
+ "schema": {
6631
+ "$ref": "#/components/schemas/PostInboxIdSparkResponse"
6632
+ }
6633
+ }
6634
+ }
6635
+ },
6636
+ "400": {
6637
+ "$ref": "#/components/responses/BadRequest"
6638
+ },
6639
+ "401": {
6640
+ "$ref": "#/components/responses/Unauthorized"
6641
+ },
6642
+ "403": {
6643
+ "$ref": "#/components/responses/Forbidden"
6644
+ },
6645
+ "404": {
6646
+ "$ref": "#/components/responses/NotFound"
6647
+ }
6648
+ },
6649
+ "security": [
6650
+ {
6651
+ "builderSession": []
6652
+ }
6653
+ ]
6654
+ },
6655
+ "delete": {
6656
+ "operationId": "delete_inbox_id_spark",
6657
+ "tags": [
6658
+ "inbox"
6659
+ ],
6660
+ "summary": "DELETE /inbox/:id/spark",
6661
+ "description": "Withdraw the Big Idea Spark mark — the reversibility half of R144. AUTHOR-ONLY like the mark itself, and legal at ANY status so a stale mark on a promoted or discarded idea can always be cleared. Writes an explicit grade='quick' row, which reads identically to no grade row at all. Un-marking a quick idea is a 200 no-op; a Full Idea is refused (409) — it is not a withdrawn spark.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
6662
+ "x-rank": "any-builder",
6663
+ "x-source": "modules/ideas/routes/inbox.js",
6664
+ "parameters": [
6665
+ {
6666
+ "name": "id",
6667
+ "in": "path",
6668
+ "required": true,
6669
+ "schema": {
6670
+ "type": "string"
6671
+ },
6672
+ "description": "Path parameter `id`."
6673
+ }
6674
+ ],
6675
+ "responses": {
6676
+ "200": {
6677
+ "description": "Success.",
6678
+ "content": {
6679
+ "application/json": {
6680
+ "schema": {
6681
+ "$ref": "#/components/schemas/DeleteInboxIdSparkResponse"
6682
+ }
6683
+ }
6684
+ }
6685
+ },
6686
+ "400": {
6687
+ "$ref": "#/components/responses/BadRequest"
6688
+ },
6689
+ "401": {
6690
+ "$ref": "#/components/responses/Unauthorized"
6691
+ },
6692
+ "403": {
6693
+ "$ref": "#/components/responses/Forbidden"
6694
+ },
6695
+ "404": {
6696
+ "$ref": "#/components/responses/NotFound"
6697
+ }
6698
+ },
6699
+ "security": [
6700
+ {
6701
+ "builderSession": []
6702
+ }
6703
+ ]
6704
+ }
6705
+ },
6604
6706
  "/instance": {
6605
6707
  "get": {
6606
6708
  "operationId": "get_instance",
@@ -13412,6 +13514,23 @@
13412
13514
  "removed"
13413
13515
  ]
13414
13516
  },
13517
+ "DeleteInboxIdSparkResponse": {
13518
+ "type": "object",
13519
+ "properties": {
13520
+ "idea": {},
13521
+ "sparked": {
13522
+ "type": "boolean"
13523
+ },
13524
+ "unchanged": {
13525
+ "type": "boolean"
13526
+ }
13527
+ },
13528
+ "required": [
13529
+ "idea",
13530
+ "sparked",
13531
+ "unchanged"
13532
+ ]
13533
+ },
13415
13534
  "DeleteMemoryFileResponse": {
13416
13535
  "type": "object",
13417
13536
  "properties": {
@@ -16855,6 +16974,23 @@
16855
16974
  ],
16856
16975
  "additionalProperties": false
16857
16976
  },
16977
+ "PostInboxIdSparkResponse": {
16978
+ "type": "object",
16979
+ "properties": {
16980
+ "idea": {},
16981
+ "sparked": {
16982
+ "type": "boolean"
16983
+ },
16984
+ "unchanged": {
16985
+ "type": "boolean"
16986
+ }
16987
+ },
16988
+ "required": [
16989
+ "idea",
16990
+ "sparked",
16991
+ "unchanged"
16992
+ ]
16993
+ },
16858
16994
  "PostInboxRequest": {
16859
16995
  "type": "object",
16860
16996
  "properties": {
@@ -18571,9 +18707,9 @@
18571
18707
  "description": "A required dependency/feature is not configured or is temporarily down."
18572
18708
  }
18573
18709
  },
18574
- "x-endpoint-count": 294,
18575
- "x-schema-count": 327,
18710
+ "x-endpoint-count": 296,
18711
+ "x-schema-count": 329,
18576
18712
  "x-undocumented-bodies": 8,
18577
- "x-response-schemas": 218,
18713
+ "x-response-schemas": 220,
18578
18714
  "x-generated-by": "scripts/gds/gen-api-docs.js"
18579
18715
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Bongos API reference
4
4
 
5
- > **Generated from the live route files** — the route file is authoritative. 294 endpoints across 47 route files.
5
+ > **Generated from the live route files** — the route file is authoritative. 296 endpoints across 47 route files.
6
6
  > Machine-readable spec: [`docs/api/openapi.json`](api/openapi.json) (OpenAPI 3.1). Rendered docs site: **`/docs`** (e.g. `cloudbongos.com/docs`).
7
7
 
8
8
  Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>.md)): `public` < `any-builder` < `metic+archon` < `archon`.
@@ -258,7 +258,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
258
258
  |---|---|---|---|---|
259
259
  | GET | `/api/bongos/healthz` | `public` | — | liveness probe; status.example.com hits this anonymously. |
260
260
 
261
- ## `inbox` (8)
261
+ ## `inbox` (10)
262
262
 
263
263
  | Method | Path | Rank | Body | Description |
264
264
  |---|---|---|---|---|
@@ -269,6 +269,8 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
269
269
  | PATCH | `/api/bongos/inbox/:id/body` | `any-builder` | `body_md` | Metic+ (task 360 / ADR 0018): idea triage (promote/discard/merge) shapes the backlog. |
270
270
  | POST | `/api/bongos/inbox/:id/ratify` | `metic+archon` | `notes_md` | V3.R31 (task 252) — Archon ratifies a criterion-proposal idea, atomically: - INSERT done_when_criteria row on the target version - UPDATE… |
271
271
  | POST | `/api/bongos/inbox/:id/resubmit` | `any-builder` | `field_values`, `template_key` | RESUBMIT a Full Idea after a rejection (R13 of goal 1000069, criterion C2: an idea never dies and never stalls). |
272
+ | POST | `/api/bongos/inbox/:id/spark` | `any-builder` | — | Mark a Quick Idea as a Big Idea Spark — "I have the spark but not the full picture, someone develop this". |
273
+ | DELETE | `/api/bongos/inbox/:id/spark` | `any-builder` | — | Withdraw the Big Idea Spark mark — the reversibility half of R144. |
272
274
  | GET | `/api/bongos/inbox/awaiting-nod` | `metic+archon` | — | GET /inbox/awaiting-nod — the NOD QUEUE (task 1003066, BV1.R05). |
273
275
 
274
276
  ## `instance` (1)
package/docs/file-map.md CHANGED
@@ -175,6 +175,7 @@
175
175
  │ ├── smoke-box.sh ← (#598) hermetic box-lifecycle smoke (no DB/DO/network): runs tests/boxes.mjs + tests/cf_tunnel.mjs (#771 named tunnel) + route-rank + module-load
176
176
  │ ├── recall.js ← `/recall` CLI: two-call progressive-disclosure search (summary → `--expand <id>`) over Bongos retrieval layer (ADR 0060); `node scripts/gds/recall.js <query>`
177
177
  │ ├── capture.js ← file an idea_inbox row from the CLI (the ideas.capture kernel port); backs the "capture a tangent" flow across skills
178
+ │ ├── spark.js ← (task 1002920 / R144) mark one of your OWN quick ideas as a Big Idea Spark — "someone develop this"; `--undo` withdraws it. Author-only server-side (the mark is the consent); its own command because sparking acts on an EXISTING idea, which is why capture.js refuses grade='spark'
178
179
  │ ├── worktree.js ← (task 1002809) junction-safe worktree add/remove/list — outside-repo worktrees get a node_modules junction via node (no shell quoting), removed BEFORE `git worktree remove`
179
180
  │ ├── learning-capture.js ← file a `learnings` row (DB-backed short-form rule capture, replacing hand-edited recipe files for a single gotcha)
180
181
  │ ├── docs-entropy.js ← ([#…] docs-entropy) markdown-corpus linter: broken internal links + cross-file duplicate-prose detector across every tracked `.md`; `--verbose` / `--file-ideas`
@@ -408,6 +408,10 @@ function createClient(opts = {}) {
408
408
  postInboxIdRatify: (args) => request("POST", "/inbox/{id}/ratify", { hasBody: true }, args),
409
409
  // POST /inbox/{id}/resubmit — rank: any-builder — POST /inbox/:id/resubmit
410
410
  postInboxIdResubmit: (args) => request("POST", "/inbox/{id}/resubmit", { hasBody: true }, args),
411
+ // DELETE /inbox/{id}/spark — rank: any-builder — DELETE /inbox/:id/spark
412
+ deleteInboxIdSpark: (args) => request("DELETE", "/inbox/{id}/spark", { hasBody: false }, args),
413
+ // POST /inbox/{id}/spark — rank: any-builder — POST /inbox/:id/spark
414
+ postInboxIdSpark: (args) => request("POST", "/inbox/{id}/spark", { hasBody: true }, args),
411
415
  // GET /inbox/awaiting-nod — rank: metic+archon — GET /inbox/awaiting-nod
412
416
  getInboxAwaitingNod: (args) => request("GET", "/inbox/awaiting-nod", { hasBody: false }, args),
413
417
  },
@@ -443,6 +443,119 @@ module.exports = function buildInboxRouter() {
443
443
  }
444
444
  });
445
445
 
446
+ // -----------------------------------------------------------------------
447
+ // The Big Idea Spark mark (task 1002920 / BV1.R144, goal 1000062)
448
+ //
449
+ // "I have the spark but not the full picture — someone develop this."
450
+ //
451
+ // AUTHOR-ONLY, AND THAT IS THE WHOLE POINT. The owner's decision: this mark IS
452
+ // the consent that makes cross-builder development fair. Without it a Quick
453
+ // Idea is simply NOT available for someone else to develop — a quick idea is
454
+ // for turning a thought into a task fast, and it is explicitly *not* an
455
+ // unfinished Full Idea waiting to be finished by a stranger. So the gate is
456
+ // not "who is senior enough", it is "whose thought is this": enforced
457
+ // in-handler against `captured_by`, the same ownership posture as
458
+ // POST /inbox/:id/resubmit above and PATCH /inbox/:id/body below.
459
+ //
460
+ // A MARK, MODELLED AS A RESOURCE: POST puts it on, DELETE takes it off. The
461
+ // done-when requires the mark be REVERSIBLE, and un-sparking is the plain
462
+ // reading of that — an author who marked a thought as needing development may
463
+ // decide they had the full picture after all. Un-sparking writes an EXPLICIT
464
+ // grade='quick' row, which the R140 design states reads identically to having
465
+ // no row at all, so a reversal leaves no residue a reader has to interpret.
466
+ //
467
+ // ONLY A QUICK IDEA CAN BE SPARKED. A Full Idea already carries the developed
468
+ // picture (the five template fields + a score); asking for it to be developed
469
+ // is a contradiction, and sparking it would hand setIdeaGrade a `spark` write
470
+ // whose empty fieldValues would WIPE that work — exactly the stale/lost-field
471
+ // hazard setIdeaGrade's whole-row contract warns about. Refused 409, before
472
+ // any write.
473
+ //
474
+ // Both directions are IDEMPOTENT: sparking a spark, or un-sparking a quick,
475
+ // reports `unchanged` at 200 rather than erroring. A double-click on the R154
476
+ // hall control is not a failure, and the caller's intent is already satisfied.
477
+ //
478
+ // rank: any-builder (requireBuilder) + the in-handler author gate.
479
+ // Pinned in route-rank-check.MODULE_EXPECTED_RANKS['ideas'].
480
+
481
+ // Shared by both directions: load the idea, prove ownership, and refuse a
482
+ // grade this mark has no business touching. Returns the idea, or null when it
483
+ // has already answered the request (so the caller just returns).
484
+ async function loadSparkable(req, res, { requireOpen }) {
485
+ const id = parseId(req, res, { code: 'bad_id', positiveInt: true });
486
+ if (id === null) return null;
487
+ const idea = await inbox.getIdea(id);
488
+ if (!idea) { res.fail('idea_not_found', 404); return null; }
489
+ // Authority first, and it leaks nothing: any builder can already read this
490
+ // idea from the inbox, so a 403 here reveals only that they are not its author.
491
+ if (idea.captured_by == null || String(idea.captured_by) !== String(req.builder.id)) {
492
+ res.fail('not_your_idea', 403, {
493
+ reason: 'the spark mark is the AUTHOR\'s consent to have their idea developed; only they can give or withdraw it',
494
+ });
495
+ return null;
496
+ }
497
+ if (idea.grade === 'full') {
498
+ res.fail('not_a_quick_idea', 409, {
499
+ grade: idea.grade,
500
+ reason: 'a Full Idea already carries the developed picture; the spark mark asks for one',
501
+ });
502
+ return null;
503
+ }
504
+ // Only MARKING requires an open idea. A promoted or discarded idea is not
505
+ // awaiting development, and admitting it would seed the R145 spark queue
506
+ // with work nobody can pick up. WITHDRAWING the mark stays legal at any
507
+ // status — clearing a stale mark is housekeeping, and blocking it would be
508
+ // the one way to leave a permanent spark on a dead idea.
509
+ if (requireOpen && idea.status !== 'open') {
510
+ res.fail('idea_not_open', 409, {
511
+ status: idea.status,
512
+ reason: 'only an open idea can be marked as awaiting development',
513
+ });
514
+ return null;
515
+ }
516
+ return idea;
517
+ }
518
+
519
+ // Mark a Quick Idea as a Big Idea Spark — "I have the spark but not the full
520
+ // picture, someone develop this". AUTHOR-ONLY: the mark is the author's consent
521
+ // to have their idea developed, so only they can set it. A Full Idea is refused
522
+ // (409) and a non-open idea cannot be newly marked; re-marking is a 200 no-op.
523
+ router.post('/inbox/:id/spark', api.requireBuilder, api.asyncHandler('POST /inbox/:id/spark', async (req, res) => {
524
+ const idea = await loadSparkable(req, res, { requireOpen: true });
525
+ if (!idea) return;
526
+ if (idea.grade === 'spark') {
527
+ return res.json({ idea, sparked: true, unchanged: true });
528
+ }
529
+ // grade only — a spark carries no template fields and no score (the R140
530
+ // migration's own shape: field_values is for a Full Idea). setIdeaGrade's
531
+ // defaults say exactly that, so they are left to speak for themselves.
532
+ const row = await inbox.setIdeaGrade(idea.id, { grade: 'spark' });
533
+ return res.json({
534
+ idea: { ...idea, grade: row.grade, template_key: row.template_key, field_values: row.field_values, completeness_score: row.completeness_score },
535
+ sparked: true,
536
+ unchanged: false,
537
+ });
538
+ }, { errorCode: 'idea_spark_failed' }));
539
+
540
+ // Withdraw the Big Idea Spark mark — the reversibility half of R144. AUTHOR-ONLY
541
+ // like the mark itself, and legal at ANY status so a stale mark on a promoted or
542
+ // discarded idea can always be cleared. Writes an explicit grade='quick' row,
543
+ // which reads identically to no grade row at all. Un-marking a quick idea is a
544
+ // 200 no-op; a Full Idea is refused (409) — it is not a withdrawn spark.
545
+ router.delete('/inbox/:id/spark', api.requireBuilder, api.asyncHandler('DELETE /inbox/:id/spark', async (req, res) => {
546
+ const idea = await loadSparkable(req, res, { requireOpen: false });
547
+ if (!idea) return;
548
+ if (idea.grade !== 'spark') {
549
+ return res.json({ idea, sparked: false, unchanged: true });
550
+ }
551
+ const row = await inbox.setIdeaGrade(idea.id, { grade: 'quick' });
552
+ return res.json({
553
+ idea: { ...idea, grade: row.grade, template_key: row.template_key, field_values: row.field_values, completeness_score: row.completeness_score },
554
+ sparked: false,
555
+ unchanged: false,
556
+ });
557
+ }, { errorCode: 'idea_unspark_failed' }));
558
+
446
559
  // Metic+ (#360 / ADR 0018): idea triage (promote/discard/merge) shapes the
447
560
  // backlog. Criterion C2 assigns triage to Metic, so this widened from
448
561
  // archon-only to metic+archon when the three-rank model went live. Any
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.293",
3
+ "version": "1.19.295",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.293",
9
+ "version": "1.19.295",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.293",
3
+ "version": "1.19.295",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",