@bongos/core 1.19.812 → 1.19.814

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.
Files changed (38) hide show
  1. package/.bongos-core.json +73 -38
  2. package/clients/bongos-client/bongos-client.global.js +1 -1
  3. package/clients/bongos-client/index.cjs +1 -1
  4. package/clients/bongos-client/index.d.ts +2 -3
  5. package/clients/bongos-client/index.mjs +1 -1
  6. package/docs/adr/0297-a-tasks-creator-may-retire-it.md +128 -0
  7. package/docs/adr/README.md +1 -0
  8. package/docs/api/openapi.json +5 -28
  9. package/docs/api-reference.md +2 -2
  10. package/docs/canonical-permissions.md +30 -1
  11. package/docs/copy-inventory.md +49 -45
  12. package/docs/copy-registry.json +86 -50
  13. package/docs/module-api-changelog.md +4 -0
  14. package/modules/dev-box/app/src/vendor/bongos-client.cjs +1 -1
  15. package/modules/government/catalog.js +28 -1
  16. package/modules/government/migrations/government_015_task_abandon_own.sql +64 -0
  17. package/modules/hall-ui/public/task.js +50 -1
  18. package/modules/lifecycle/abandon-authz.js +132 -0
  19. package/modules/lifecycle/db-executor.js +45 -0
  20. package/modules/lifecycle/db-goals.js +29 -7
  21. package/modules/lifecycle/db-ship.js +59 -5
  22. package/modules/lifecycle/db-tasks.js +11 -5
  23. package/modules/lifecycle/db-versions.js +17 -3
  24. package/modules/lifecycle/done-when.js +5 -2
  25. package/modules/lifecycle/goal-edits.js +5 -1
  26. package/modules/lifecycle/routes/abandon-handler.js +151 -0
  27. package/modules/lifecycle/routes/done-when.js +150 -86
  28. package/modules/lifecycle/routes/tasks.js +26 -38
  29. package/package-lock.json +2 -2
  30. package/package.json +1 -1
  31. package/src/bongos/route-rank-check.js +12 -1
  32. package/src/module-api.js +1 -1
  33. package/tests/abandon_creator_authz.mjs +176 -0
  34. package/tests/abandon_route.mjs +276 -0
  35. package/tests/abandon_task.mjs +145 -3
  36. package/tests/criterion_authoring_gate.mjs +22 -1
  37. package/tests/criterion_goal_attach.mjs +233 -8
  38. package/tests/government_ownership_scope.mjs +13 -7
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.812",
6
- "core_contract": "1.19.812",
7
- "source_commit": "b0747d044e08c53ffda8cc93bceafef59787e27a",
5
+ "core_version": "1.19.814",
6
+ "core_contract": "1.19.814",
7
+ "source_commit": "1f01e14964d5ea0ba6bc679b6545e33b7858829c",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-17T06:53:39.832Z",
9
+ "built_at": "2026-09-17T07:24:36.013Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 488,
12
+ "docs_redacted": 489,
13
13
  "agent_docs_stubbed": 26,
14
- "functional_verbatim": 2208,
14
+ "functional_verbatim": 2214,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2722,
20
- "tree_sha256": "7ce723b60b68f0cb665f81b47e71bdcec72bd1ddc5fe8c4e22c9ad0a07b79440",
19
+ "file_count": 2729,
20
+ "tree_sha256": "7a2511fd82d8a030199059a73c74669bc0c447fb5860e4eca010231226cd32a1",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -352,7 +352,7 @@
352
352
  {
353
353
  "path": "clients/bongos-client/bongos-client.global.js",
354
354
  "mode": "0000644",
355
- "sha256": "979dfc61bb05eba94d1b8caa6c5719c8b0eb083aa3a0f08b948cf4664f3f4450"
355
+ "sha256": "5753830b87cce7612cb6c6799ea3807fa5c2e9eaf1b1e5f137af21fb3692a3c4"
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": "fe53f128f95908a58ebe749496d0dde631c4be5295c776cafd26a0d7b37e3102"
365
+ "sha256": "233ade028d8fb2334e0cd2e17d10d695a5c873a8ebe5589407cccac6dd023bd5"
366
366
  },
367
367
  {
368
368
  "path": "clients/bongos-client/index.d.ts",
369
369
  "mode": "0000644",
370
- "sha256": "5b6c3210bb423ef975ea9ad8e45bd0b24f93b368b4160f22f153f1b23da5c4ae"
370
+ "sha256": "e713625480afabdcda10f665beabb91cccaa3bce0268371a6ee780daea626b6d"
371
371
  },
372
372
  {
373
373
  "path": "clients/bongos-client/index.mjs",
374
374
  "mode": "0000644",
375
- "sha256": "38011ff56e228ded3d526bcb50b2c0c2b8b8ab7a742dffb16f377983c37ea676"
375
+ "sha256": "e8592cf9783404ce849e1c234a06c716993ef9c9f4d5b066a456eeeec002b0c0"
376
376
  },
377
377
  {
378
378
  "path": "clients/bongos-client/package.json",
@@ -1969,20 +1969,25 @@
1969
1969
  "mode": "0000644",
1970
1970
  "sha256": "440116d384964c06055b1c7c07c30a962b164f519c9bbf3ce910570d186f1308"
1971
1971
  },
1972
+ {
1973
+ "path": "docs/adr/0297-a-tasks-creator-may-retire-it.md",
1974
+ "mode": "0000644",
1975
+ "sha256": "4e6d5268c58e905c86acc1bbd4c66778e3695ecd37cbc1c9e0f1448cd67f26a9"
1976
+ },
1972
1977
  {
1973
1978
  "path": "docs/adr/README.md",
1974
1979
  "mode": "0000644",
1975
- "sha256": "b59f2fe025d6f96790faabe77a417fe08a42eaa29f38e176aa90ca9950d3a4a8"
1980
+ "sha256": "9e305c448d390efbfd7f4b5521284a8dc452d43a106d4686a0176433e2d1a3a7"
1976
1981
  },
1977
1982
  {
1978
1983
  "path": "docs/api-reference.md",
1979
1984
  "mode": "0000644",
1980
- "sha256": "e7dfc7ab1f840c9a5e2ab84690f35d17c7ddcc12bca9aa974a55a39105a73df1"
1985
+ "sha256": "16b812b3d5df71ecd9c8908fcd724345cee9ebe36bdf445fedd623e8d3572498"
1981
1986
  },
1982
1987
  {
1983
1988
  "path": "docs/api/openapi.json",
1984
1989
  "mode": "0000644",
1985
- "sha256": "ccd869f8386340558cce007a74ba818d6808ac4037be0005fd81cf665b769f65"
1990
+ "sha256": "d062ab3925ae7919f1f0e32a2da4974cae41ee409d914ea6fb49bfd3591b91d2"
1986
1991
  },
1987
1992
  {
1988
1993
  "path": "docs/architecture.md",
@@ -2002,17 +2007,17 @@
2002
2007
  {
2003
2008
  "path": "docs/canonical-permissions.md",
2004
2009
  "mode": "0000644",
2005
- "sha256": "37d1a7cbbdaf86e4814ebb8c878b7d1e99021a81a147513c5cbd7807d6d7216a"
2010
+ "sha256": "bbc4878ab6e3a0cedf5fad1b197d427b44d89ea2d32fd8dce9babfe73da09ee1"
2006
2011
  },
2007
2012
  {
2008
2013
  "path": "docs/copy-inventory.md",
2009
2014
  "mode": "0000644",
2010
- "sha256": "d7d32a0abf958fc99e21516e1708f37015376c456e0ffc125ab22a53d9a07994"
2015
+ "sha256": "b0fb481aa86ab8ad8b2bf37bfdf09e6aba00f611156d92002632384cddc40cb2"
2011
2016
  },
2012
2017
  {
2013
2018
  "path": "docs/copy-registry.json",
2014
2019
  "mode": "0000644",
2015
- "sha256": "5a7576210021053f21acc7d7cc6fe90d71b603bbb2fcc118a09ef64e40bbc750"
2020
+ "sha256": "a21e7da139c5593f452509692e3f17b945f99eb5013f9a0e7ec4ceedc8c8b50e"
2016
2021
  },
2017
2022
  {
2018
2023
  "path": "docs/design/apex-pass-2-direction.md",
@@ -2537,7 +2542,7 @@
2537
2542
  {
2538
2543
  "path": "docs/module-api-changelog.md",
2539
2544
  "mode": "0000644",
2540
- "sha256": "3478aad2e6df54281dea8fcab1695cb6f943da2d3b9f5fe2ca9a6ec902061d5b"
2545
+ "sha256": "2e01c98f75c74643cc843b80f507635d1d674749fed9185414cfce51eeac3cf8"
2541
2546
  },
2542
2547
  {
2543
2548
  "path": "docs/modules-contract.md",
@@ -4072,7 +4077,7 @@
4072
4077
  {
4073
4078
  "path": "modules/dev-box/app/src/vendor/bongos-client.cjs",
4074
4079
  "mode": "0000644",
4075
- "sha256": "fe53f128f95908a58ebe749496d0dde631c4be5295c776cafd26a0d7b37e3102"
4080
+ "sha256": "233ade028d8fb2334e0cd2e17d10d695a5c873a8ebe5589407cccac6dd023bd5"
4076
4081
  },
4077
4082
  {
4078
4083
  "path": "modules/dev-box/app/test/unit.mjs",
@@ -4322,7 +4327,7 @@
4322
4327
  {
4323
4328
  "path": "modules/government/catalog.js",
4324
4329
  "mode": "0000644",
4325
- "sha256": "25f086b8b845c35a3d9fbefbf1dcc30f0c4856b08bafc4f2c0c4952e6f57a7ab"
4330
+ "sha256": "409e2309bdfcf4b3bb60cfde69e8854af9cd280a205cca2f7640103e08a56c84"
4326
4331
  },
4327
4332
  {
4328
4333
  "path": "modules/government/config.js",
@@ -4444,6 +4449,11 @@
4444
4449
  "mode": "0000644",
4445
4450
  "sha256": "0ddff91ec6d4c1f4344c6fdc2bd2f158e34a9b14662a100cf73796924b7c98de"
4446
4451
  },
4452
+ {
4453
+ "path": "modules/government/migrations/government_015_task_abandon_own.sql",
4454
+ "mode": "0000644",
4455
+ "sha256": "d376d2532b942c77a8ad71f6bdbeea389db3276ee86a6e57a58eed41439b6bdd"
4456
+ },
4447
4457
  {
4448
4458
  "path": "modules/government/module.json",
4449
4459
  "mode": "0000644",
@@ -5327,7 +5337,7 @@
5327
5337
  {
5328
5338
  "path": "modules/hall-ui/public/task.js",
5329
5339
  "mode": "0000644",
5330
- "sha256": "394cc4c35e789cf25825c945dd7d0aa335b9d3949a64e33d607048b4861b246c"
5340
+ "sha256": "49cd43aa6da51f6b30efac92e27912b44589c22cf879ef62b4201ed8e8552996"
5331
5341
  },
5332
5342
  {
5333
5343
  "path": "modules/hall-ui/public/task.states.json",
@@ -5469,6 +5479,11 @@
5469
5479
  "mode": "0000644",
5470
5480
  "sha256": "7cb9dac9cb87b679e1ae50e4112b3a7928a38ebb9902c8947a74d17bec4d9009"
5471
5481
  },
5482
+ {
5483
+ "path": "modules/lifecycle/abandon-authz.js",
5484
+ "mode": "0000644",
5485
+ "sha256": "4aad5d4d75fb329ae721b06840248d03c47159c632f14e22c2566ded36c158f8"
5486
+ },
5472
5487
  {
5473
5488
  "path": "modules/lifecycle/abandon-leftovers.js",
5474
5489
  "mode": "0000644",
@@ -5534,10 +5549,15 @@
5534
5549
  "mode": "0000644",
5535
5550
  "sha256": "76ffab3052ce0b48d0b78451a2e34d5cd3c5300410f0856b4a6667cbe708ba2b"
5536
5551
  },
5552
+ {
5553
+ "path": "modules/lifecycle/db-executor.js",
5554
+ "mode": "0000644",
5555
+ "sha256": "b810e4488b35fe70508cae1c6583112033def4726d71af2e7efc7b18c495ba27"
5556
+ },
5537
5557
  {
5538
5558
  "path": "modules/lifecycle/db-goals.js",
5539
5559
  "mode": "0000644",
5540
- "sha256": "2d9b4d62072ec588446d8098a08a2a300df42fa93e62b39086e80da1a95f1141"
5560
+ "sha256": "b4d1c8bd2254f93519a88302072c2f524a1acb4152382fb09bdc22b1ef95f0ed"
5541
5561
  },
5542
5562
  {
5543
5563
  "path": "modules/lifecycle/db-grade.js",
@@ -5562,17 +5582,17 @@
5562
5582
  {
5563
5583
  "path": "modules/lifecycle/db-ship.js",
5564
5584
  "mode": "0000644",
5565
- "sha256": "5b4e370fcaf6a5d23f2835833d4cd77eab67b9d509f78c24dd2650efa8e97fac"
5585
+ "sha256": "19e70254936af3f51fa12d3b9aa9d25dbc0a84761aee84b5143ebfe6a8654b30"
5566
5586
  },
5567
5587
  {
5568
5588
  "path": "modules/lifecycle/db-tasks.js",
5569
5589
  "mode": "0000644",
5570
- "sha256": "b26ba2c82bea0ee71e3db888bf531cee605aac90fa5ded2bcffe548cce650f4f"
5590
+ "sha256": "87b075187493e8026f99912dceb7195d09564bfe3f969f344141661c60ef6558"
5571
5591
  },
5572
5592
  {
5573
5593
  "path": "modules/lifecycle/db-versions.js",
5574
5594
  "mode": "0000644",
5575
- "sha256": "7eaaafc9414bf77c12c57eefde02ddf1c67601f4b6b15ded3692bc3765ac036b"
5595
+ "sha256": "4b0681bf2bc7cd40a0a7bcdc9d2fab033c735390555be54b0a1083e5fa231abd"
5576
5596
  },
5577
5597
  {
5578
5598
  "path": "modules/lifecycle/db.js",
@@ -5587,7 +5607,7 @@
5587
5607
  {
5588
5608
  "path": "modules/lifecycle/done-when.js",
5589
5609
  "mode": "0000644",
5590
- "sha256": "186a830fbb63ff5510af78ba4fe77cd94325571eed2809ace7817c66fb4155ba"
5610
+ "sha256": "200e7c0937fefb97703927a699bff40b9c5061fae29c6779b998542a00724a22"
5591
5611
  },
5592
5612
  {
5593
5613
  "path": "modules/lifecycle/est-advisory.js",
@@ -5627,7 +5647,7 @@
5627
5647
  {
5628
5648
  "path": "modules/lifecycle/goal-edits.js",
5629
5649
  "mode": "0000644",
5630
- "sha256": "f00d1bb6bd57624679295f33d55ad1d81a9479789a78d4325f13948b9559f9f5"
5650
+ "sha256": "c22a0782ddf7a93deb40f700a2afab15a313925647bbdf424db777da5aa29aa1"
5631
5651
  },
5632
5652
  {
5633
5653
  "path": "modules/lifecycle/goal-rollup.js",
@@ -5734,6 +5754,11 @@
5734
5754
  "mode": "0000644",
5735
5755
  "sha256": "74ca9df69cd8d119c59cccf5976a3ab99c8cc1e85414488ad432c445ddd821e2"
5736
5756
  },
5757
+ {
5758
+ "path": "modules/lifecycle/routes/abandon-handler.js",
5759
+ "mode": "0000644",
5760
+ "sha256": "64e1baa060af63f7242d7164c7e9711aa1230ef003d1aa3f51c91f392e840725"
5761
+ },
5737
5762
  {
5738
5763
  "path": "modules/lifecycle/routes/analytics.js",
5739
5764
  "mode": "0000644",
@@ -5762,7 +5787,7 @@
5762
5787
  {
5763
5788
  "path": "modules/lifecycle/routes/done-when.js",
5764
5789
  "mode": "0000644",
5765
- "sha256": "9370fd1fc8a4a1ff864c1e85321b05a74bb45150ef8eb92cefdbbd85524bbe27"
5790
+ "sha256": "13acccfc01be1760dc016519ccb430c448b237d6e02db83aa01815b2e45db7de"
5766
5791
  },
5767
5792
  {
5768
5793
  "path": "modules/lifecycle/routes/gate-approvals.js",
@@ -5827,7 +5852,7 @@
5827
5852
  {
5828
5853
  "path": "modules/lifecycle/routes/tasks.js",
5829
5854
  "mode": "0000644",
5830
- "sha256": "0497ab99a5c13399617b43f70d775e77773f1eed259548433f19c20c708ebab9"
5855
+ "sha256": "06948b8df847145848dec75dcb3c525cecd24948064eae19ae524e0498576b2c"
5831
5856
  },
5832
5857
  {
5833
5858
  "path": "modules/lifecycle/routes/version-route-authz.js",
@@ -7767,12 +7792,12 @@
7767
7792
  {
7768
7793
  "path": "package-lock.json",
7769
7794
  "mode": "0000644",
7770
- "sha256": "2a7a58719ee7045ba87756d837dd6c059a099e04b4e86ee4a5c8cc9e2c42b50e"
7795
+ "sha256": "bc7f846af00793ffc9d682071391f110593648a25297cd15050385b9c3a8828f"
7771
7796
  },
7772
7797
  {
7773
7798
  "path": "package.json",
7774
7799
  "mode": "0000644",
7775
- "sha256": "46f70444019c4bb61f50098fb4e9d1f2b05cc66142c6f59ea36a17c8e722e636"
7800
+ "sha256": "db09d4c1609d9b4b0547b052326785367cfe19a5838cd3f9d458dac40ef70685"
7776
7801
  },
7777
7802
  {
7778
7803
  "path": "public-docs/index.html",
@@ -9527,7 +9552,7 @@
9527
9552
  {
9528
9553
  "path": "src/bongos/route-rank-check.js",
9529
9554
  "mode": "0000644",
9530
- "sha256": "f57293c2d157949ccb5a74511f167902adf096037b4830b0bf667a38baf6ab58"
9555
+ "sha256": "909c8ea1b1f73ece215ada3cc41488590a0558879af7b3850e55cbf2729b719c"
9531
9556
  },
9532
9557
  {
9533
9558
  "path": "src/bongos/routes.js",
@@ -9662,7 +9687,7 @@
9662
9687
  {
9663
9688
  "path": "src/module-api.js",
9664
9689
  "mode": "0000644",
9665
- "sha256": "7532469124e010639f2fcf10257a9ffad594d90f3a15a67647c9a9c052ded87e"
9690
+ "sha256": "2b104850c999f4a9c71f7f205c640b9525a9a812031c1bad6d13cdcd06c05706"
9666
9691
  },
9667
9692
  {
9668
9693
  "path": "src/module-loader/catalog.js",
@@ -9714,15 +9739,25 @@
9714
9739
  "mode": "0000644",
9715
9740
  "sha256": "2e46d071e5c61d302f34507215dd0a5f20a0887841ae0f7b033be865c2cb8af5"
9716
9741
  },
9742
+ {
9743
+ "path": "tests/abandon_creator_authz.mjs",
9744
+ "mode": "0000644",
9745
+ "sha256": "fb0611fe900cb213344b52c50c5f28543b5f5986b4a227cf4fcb380512a3a167"
9746
+ },
9717
9747
  {
9718
9748
  "path": "tests/abandon_leftovers.mjs",
9719
9749
  "mode": "0000644",
9720
9750
  "sha256": "fe9562bd27fc127f5113de4316d1abe784874aa198d87be79caa302d526ecf17"
9721
9751
  },
9752
+ {
9753
+ "path": "tests/abandon_route.mjs",
9754
+ "mode": "0000644",
9755
+ "sha256": "523fe88b9582153d56c0867dc2973fb89a6b9dbc010066e57ca8dba2265a0b6d"
9756
+ },
9722
9757
  {
9723
9758
  "path": "tests/abandon_task.mjs",
9724
9759
  "mode": "0000644",
9725
- "sha256": "ccfb5d3e85fd6bd34acc78d510edf20d037d344eca58706f3e3130fa6dce611c"
9760
+ "sha256": "bcf19d27a76fd9fbafc3791f6a46d9636453427dc1c50940069673781eb817bc"
9726
9761
  },
9727
9762
  {
9728
9763
  "path": "tests/access_requests_invite.mjs",
@@ -10527,12 +10562,12 @@
10527
10562
  {
10528
10563
  "path": "tests/criterion_authoring_gate.mjs",
10529
10564
  "mode": "0000644",
10530
- "sha256": "970db80cf43dba855bf9bd331434add458ab16e719f06710487094a3a578ec53"
10565
+ "sha256": "ebe63de3ffcc33b5848e4226c5d2746d0662a04b9997518469ee98c9515e25d4"
10531
10566
  },
10532
10567
  {
10533
10568
  "path": "tests/criterion_goal_attach.mjs",
10534
10569
  "mode": "0000644",
10535
- "sha256": "979589fd08dc41da3c837513452353aa87f7653620fad22c8a9115d4d7d13b90"
10570
+ "sha256": "58a0685287cb34b1cad88d0b0d0024e801068f8a7f07f53702857076c554bcad"
10536
10571
  },
10537
10572
  {
10538
10573
  "path": "tests/criterion_progress.mjs",
@@ -11222,7 +11257,7 @@
11222
11257
  {
11223
11258
  "path": "tests/government_ownership_scope.mjs",
11224
11259
  "mode": "0000644",
11225
- "sha256": "76b81db7769290d5ef5c26ece86ce0610c5aef6c8984ffdd54a6340db66db3bc"
11260
+ "sha256": "a05a1aaec7b6924bc1653307f921b16ec9517191bf51956c968c4d53eec0e988"
11226
11261
  },
11227
11262
  {
11228
11263
  "path": "tests/government_page_gates.mjs",
@@ -899,7 +899,7 @@ function createClient(opts = {}) {
899
899
  getTasksId: (args) => request("GET", "/tasks/{id}", { hasBody: false }, args),
900
900
  // PATCH /tasks/{id} — rank: metic+archon — PATCH /tasks/:id
901
901
  patchTasksId: (args) => request("PATCH", "/tasks/{id}", { hasBody: true }, args),
902
- // POST /tasks/{id}/abandon — rank: metic+archon — POST /tasks/:id/abandon
902
+ // POST /tasks/{id}/abandon — rank: any-builder — POST /tasks/:id/abandon
903
903
  postTasksIdAbandon: (args) => request("POST", "/tasks/{id}/abandon", { hasBody: true }, args),
904
904
  // POST /tasks/{id}/attest-gate — rank: any-builder — POST /tasks/:id/attest-gate
905
905
  postTasksIdAttestGate: (args) => request("POST", "/tasks/{id}/attest-gate", { hasBody: true }, args),
@@ -898,7 +898,7 @@ function createClient(opts = {}) {
898
898
  getTasksId: (args) => request("GET", "/tasks/{id}", { hasBody: false }, args),
899
899
  // PATCH /tasks/{id} — rank: metic+archon — PATCH /tasks/:id
900
900
  patchTasksId: (args) => request("PATCH", "/tasks/{id}", { hasBody: true }, args),
901
- // POST /tasks/{id}/abandon — rank: metic+archon — POST /tasks/:id/abandon
901
+ // POST /tasks/{id}/abandon — rank: any-builder — POST /tasks/:id/abandon
902
902
  postTasksIdAbandon: (args) => request("POST", "/tasks/{id}/abandon", { hasBody: true }, args),
903
903
  // POST /tasks/{id}/attest-gate — rank: any-builder — POST /tasks/:id/attest-gate
904
904
  postTasksIdAttestGate: (args) => request("POST", "/tasks/{id}/attest-gate", { hasBody: true }, args),
@@ -399,7 +399,6 @@ export interface PostSsoTokenRequest { client_id: string; client_secret: string;
399
399
  export interface PostSsoTokenResponse { github_id: unknown; github_login: unknown; display_name: unknown; avatar_url: unknown }
400
400
  export interface PostTaskRecommendationsRequest { task_id: number; recommended_to: number; reason: string }
401
401
  export interface PostTaskRecommendationsResponse { ok: boolean; recommendation: unknown }
402
- export interface PostTasksIdAbandonRequest { reason?: string }
403
402
  export interface PostTasksIdAttestGateRequest { head_sha: string }
404
403
  export interface PostTasksIdAttestGateResponse { ok: boolean; attested: unknown; reason: unknown }
405
404
  export interface PostTasksIdConfirmRequest { verified?: unknown }
@@ -1262,8 +1261,8 @@ export interface BongosClient {
1262
1261
  getTasksId(args?: RequestArgs): Promise<ApiResponse>;
1263
1262
  /** PATCH /tasks/{id} — rank: metic+archon */
1264
1263
  patchTasksId(args?: RequestArgs & { body?: PatchTasksIdRequest }): Promise<ApiResponse>;
1265
- /** POST /tasks/{id}/abandon — rank: metic+archon */
1266
- postTasksIdAbandon(args?: RequestArgs & { body?: PostTasksIdAbandonRequest }): Promise<ApiResponse>;
1264
+ /** POST /tasks/{id}/abandon — rank: any-builder */
1265
+ postTasksIdAbandon(args?: RequestArgs): Promise<ApiResponse>;
1267
1266
  /** POST /tasks/{id}/attest-gate — rank: any-builder */
1268
1267
  postTasksIdAttestGate(args: RequestArgs & { body: PostTasksIdAttestGateRequest }): Promise<PostTasksIdAttestGateResponse>;
1269
1268
  /** POST /tasks/{id}/confirm — rank: any-builder */
@@ -895,7 +895,7 @@ export function createClient(opts = {}) {
895
895
  getTasksId: (args) => request("GET", "/tasks/{id}", { hasBody: false }, args),
896
896
  // PATCH /tasks/{id} — rank: metic+archon — PATCH /tasks/:id
897
897
  patchTasksId: (args) => request("PATCH", "/tasks/{id}", { hasBody: true }, args),
898
- // POST /tasks/{id}/abandon — rank: metic+archon — POST /tasks/:id/abandon
898
+ // POST /tasks/{id}/abandon — rank: any-builder — POST /tasks/:id/abandon
899
899
  postTasksIdAbandon: (args) => request("POST", "/tasks/{id}/abandon", { hasBody: true }, args),
900
900
  // POST /tasks/{id}/attest-gate — rank: any-builder — POST /tasks/:id/attest-gate
901
901
  postTasksIdAttestGate: (args) => request("POST", "/tasks/{id}/attest-gate", { hasBody: true }, args),
@@ -0,0 +1,128 @@
1
+ # ADR 0297 — A task's creator may retire it, and that is the ownership axis's first route
2
+
3
+ **Status:** accepted · **Date:** 2026-09-16 · **Task:** [task 1002443](https://cloudbongos.com/builders#/task/1002443) (goal 1000095)
4
+
5
+ ## Context
6
+
7
+ The owner, 2026-07-25: *"Each task should have a creator, the creator of that task
8
+ should be able to abandon that task directly inside of the UI."*
9
+
10
+ The first half shipped elsewhere and earlier. `migrations/core_227_task_created_by.sql`
11
+ ([task 1003278](https://cloudbongos.com/builders#/task/1003278), goal 1000072) added
12
+ `tasks.created_by` — nullable, no backfill, indexed, with the two `SELECT t.*` views
13
+ rebuilt so the column actually reaches the claimable feed — and `createTask` has
14
+ threaded the caller through ever since. This ADR is only about the second half, which
15
+ had not moved: **retiring a task was a rank power and nothing else.**
16
+
17
+ `POST /tasks/:id/abandon` gated on `task.abandon`, floor `metic`
18
+ ([ADR 0152](0152-metic-task-abandonment.md), archon before that under
19
+ [ADR 0090](0090-metic-task-authoring.md)). So a builder below Metic could file a
20
+ task and then could not withdraw it. They had to ask someone with triage authority to
21
+ retire a proposal that was theirs in the first place — the same shape as the goal
22
+ membership problem [ADR 0106](0106-goal-owner-manager-authority.md) fixed, and
23
+ for the same reason: **the rule is about whose row it is, and a rank cannot say that.**
24
+
25
+ The permission model had already grown the vocabulary for this and had never used it.
26
+ [ADR 0151](0151-governance-permissions-as-atom-ranks-as-roles.md) §3 defines an **ownership axis** —
27
+ a key declared `scope: 'own'` is *parameterized by a concrete row*, so holding it
28
+ authorizes nothing until a handler that has loaded that row supplies its owner. R100
29
+ made `requirePermission` refuse an own-scoped key outright, precisely so `.own` could
30
+ never degrade into `.any`, and recorded that **zero call sites passed one**. The
31
+ resolver shipped `authorizeOwned` / `authorizeOwnedOrAny`; six keys were declared
32
+ own-scoped; not one route decided anything with them. This is that axis's first real
33
+ consumer.
34
+
35
+ ## Decision
36
+
37
+ **Two authorities reach the abandon transition, and only one of them is rank-shaped.**
38
+
39
+ | key | floor | reaches |
40
+ |---|---|---|
41
+ | `task.abandon` | `metic` | any task, any creator — triage authority, unchanged from ADR 0152 |
42
+ | `task.abandon.own` | `xenos` | a task you created, while it is still `backlog` / `ready` / `blocked` |
43
+
44
+ The route is therefore `requireBuilder` plus an in-handler decision — the shape
45
+ `POST /goals/:id/members`, `PATCH /goals/:id/members/:builderId` and
46
+ `PATCH /inbox/:id/body` already use, and the shape `requirePermission` structurally
47
+ cannot express. Its rank pin moves `metic+archon` → `any-builder`, whose job becomes
48
+ catching an accidental **tightening** back to a rank floor as loudly as it catches a
49
+ gate removal.
50
+
51
+ **The two facts are resolved in two different places, on purpose.** Authority here is a
52
+ conjunction drawn from two sources, and folding them together loses one:
53
+
54
+ - the **rank fact** — does this builder hold the key — is request-scoped, so it resolves
55
+ in the route before the transaction opens: once, uncached, per
56
+ [ADR 0016](0016-trust-boundary-server-enforced-permissions.md);
57
+ - the **owner fact** — is this row theirs — is a column that can change under us, so it
58
+ is evaluated *inside* `abandonTask`'s existing `FOR UPDATE` transaction, through a new
59
+ optional `authorize` callback. `created_by` joins the locked `SELECT`, so the predicate
60
+ and the `UPDATE` read the same row version.
61
+
62
+ Reading `created_by` in the route would be a TOCTOU window — the task can be
63
+ re-attributed, claimed or shipped between the check and the write. Resolving permissions
64
+ inside the transaction would hold a row lock across a second pool acquire, which under a
65
+ saturated pool is a deadlock. Splitting them is what makes the decision both **fresh**
66
+ and **race-free**, and it is why the callback is documented as pure and DB-free rather
67
+ than merely happening to be.
68
+
69
+ **The decision itself is a pure module**, `modules/lifecycle/abandon-authz.js`, following
70
+ `goal-route-authz.js` and `shouldRefuseSessionClaim`. Every branch is pinned by a
71
+ DB-free unit test instead of only through a live route, and the hall's display hint and
72
+ the server's verdict are demonstrably reading one rule.
73
+
74
+ **A creator's window is narrower than a Metic's, and stops at the grader.**
75
+ `backlog` / `ready` / `blocked` only. Past that a retire is a correction to the ledger
76
+ rather than an author changing their mind, so `completed` and `confirmed` stay with the
77
+ cross-owner authority. `active` is excluded here and again by the active-claim guard.
78
+ Nothing is subtracted from `task.abandon`: a Metic keeps every status they could reach
79
+ before, and a test pins that the new window never becomes a ceiling on the old power.
80
+
81
+ **`counterpart: ['task.abandon']` is declared, not derived.** Same `act` verb, same
82
+ `task` resource, so the cross-owner key genuinely subsumes retiring your own and
83
+ `authorizeOwnedOrAny` may admit on it without consulting the owner. The verb is `act`
84
+ rather than `manage` deliberately: `manage` is the full-control verb and would silently
85
+ dominate every future narrower `task.*.own` key — the verb-blind escalation
86
+ [task 1003195](https://cloudbongos.com/builders#/task/1003195) closed, where a
87
+ `memory.read.any` grant satisfied a `memory.manage.own` write.
88
+
89
+ ## Consequences
90
+
91
+ **A null creator belongs to nobody, and that is load-bearing.** Every task filed before
92
+ `core_227` (~1,357 of them) plus the seed, restock and Discord-bug vectors carry
93
+ `created_by IS NULL`. The owner comparison is the government resolver's own — injected,
94
+ never re-implemented, because a duplicated ownership comparison is exactly how one
95
+ quietly widens — and it matches `null`, `''`, objects and `NaN` against nothing. Were
96
+ that to slip, one xenos-floor grant would open every historical task at once. It is the
97
+ single widest failure mode here and has its own test.
98
+
99
+ **The hall shows the control to a creator, and pre-answers the refusals in words.** A
100
+ task's creator sees "Retire this task…" on their own record; outside the window they are
101
+ told why rather than shown a button that would 403. The list of statuses is restated in
102
+ the browser file because it cannot `require` the server's, and is marked as a display
103
+ mirror the server re-decides under a lock.
104
+
105
+ **Fail-closed at every new branch.** A missing `government` port denies (its absence
106
+ means authority cannot be *established*, and admitting there would make disabling a
107
+ module a privilege escalation — the inversion ADR 0016 exists to prevent); a throwing
108
+ resolver denies; anything that is not an explicit `{ ok: true }` denies; `authorize`
109
+ runs *before* the already-abandoned idempotent return and before the shipped and
110
+ active-claim guards, so an unauthorized caller cannot probe a row's state through a
111
+ 200 or distinguish the 409s.
112
+
113
+ **No guard was removed.** Shipped stays immutable, an active claim still has to be
114
+ released first, the re-abandon is still idempotent, `/promote` still restores, and the
115
+ audit_log entry is unchanged on both paths.
116
+
117
+ **What this does not do.** It does not give creators any other power over their own
118
+ tasks — editing, promoting and dependency management keep their Metic floor, and
119
+ widening those is a separate decision. It does not touch `task.merge`, which reuses
120
+ `abandonTask` under the hood but is a triage judgement about duplicates rather than
121
+ authorship. And it does not backfill attribution: the control simply does not appear on
122
+ tasks that predate `core_227`, which is the honest outcome of that migration's own
123
+ refusal to guess.
124
+
125
+ **Revisit if** a second `task.*.own` key appears (the `act` verb choice is what keeps
126
+ that safe, and a wider relation would need a `VERB_DOMINANCE` entry stating why), or if
127
+ goal leadership should confer shaping authority — deliberately left conferring nothing
128
+ here, as [task 1002858](https://cloudbongos.com/builders#/task/1002858) also left it.
@@ -388,3 +388,4 @@ This keeps the decision history honest and traceable.
388
388
  | 0294 | [**CLI sessions do not expire; revocation is the whole remedy** ([task 1003956](https://cloudbongos.com/builders#/task/1003956), goal 1000110 — owner request 2026-09-16, after a three-week gap signed him out). A `bongos` sign-in died after 24h idle: `createSession` defaulted a null `expiresAt` to `now()+SESSION_TTL_MS` and `lookupSession` rewrote `expires_at = now()+24h` on every request. **This is the third argument about the number and the first about the mechanism** — V3.R38 ([task #259](https://cloudbongos.com/builders#/task/259), migration 035) set 1h, V3.R90 ([task 1000400](https://cloudbongos.com/builders#/task/1000400)) widened it to 24h because 1h "kicked builders out over lunch", and each round shortened the nuisance while leaving the mechanism alone. The hygiene case for keeping it (criterion C9 — bound a leaked token in the mode-0600 plaintext session file) **does not survive contact with refresh-on-use**: a leaked token is by definition one somebody is using, and every use rolled the expiry forward another day, so the timer could only ever fire on a session gone quiet — the legitimate holder stepping away. The control was aimed at attackers and landed exclusively on users. [ADR 0206](0206-own-scoped-session-revocation-no-absolute-ceiling.md) had already written half of this down ("a CLI bearer in daily use is, in practice, immortal") without drawing the conclusion. Decision: **CLI sessions carry no expiry and last until revoked.** `createSession` records `expires_at = NULL` when the caller names none — every such call site is `source: 'cli'` (device flow, federated sign-in, hall re-issue, dev-box pair) — and `lookupSession` still bumps `last_used_at` but rolls `expires_at` forward only via `CASE WHEN expires_at IS NULL THEN NULL ELSE …`; **without that CASE the first request after sign-in re-mints the very timeout this removes.** Migration `core_243` clears `expires_at` on live unrevoked `source='cli'` rows, so the change reaches existing sessions instead of charging every builder one last forced re-auth on deploy day — migration 035's grace inverted, for the reason 035 gave. **Browser sessions are untouched** (they pass an explicit 30 days, so the new default never reaches them, and the migration's `source='cli'` filter is its whole safety property — `lookupSession` only rolls an expiry it can already see, so a web row cleared by mistake stays cleared forever). `SESSION_TTL_MS` survives as the roll-forward window for rows that have an expiry, no longer as a default. The accepted cost, stated plainly: **a leaked CLI token is valid until revoked** — smaller in practice than it sounds, since under refresh-on-use an actively-used leak was already immortal, so what changes is the *abandoned* copy on a dead laptop. The remedy is ADR 0206's own-scoped revoke, immediate where a TTL is next-day, and that ADR's visibility half (`created_at` beside `last_used_at`) is now the primary control rather than a nice-to-have. Nothing about rank moves: `setBuilderRank` still sweeps on demotion and `requireBuilder` still refuses a deactivated builder on the hot path, neither consulting `expires_at`. Rejected: widening the TTL a fourth time; keeping it and making re-auth cheaper (already built — [task 1000401](https://cloudbongos.com/builders#/task/1000401), [task 1000504](https://cloudbongos.com/builders#/task/1000504) — the friction shrank, the interruption stayed); an absolute `created_at` ceiling (ADR 0206's reasoning holds unchanged); dropping the column (load-bearing for web); a blanket `UPDATE … SET expires_at = NULL`. Revisit for machine principals nobody will notice a leak on, a compliance maximum, or long-dead sessions accumulating faster than people prune them.](0294-cli-sessions-do-not-expire.md) | auth / account security / CLI |
389
389
  | 0295 | [**The registry is the witness the publish lane cannot be** ([task 1003916](https://cloudbongos.com/builders#/task/1003916), goal 1000090). On 2026-09-13 eight publish runs failed over three hours and NOTHING reported it; it was found by a person idly reading the npm version list. Task 1003914 fixed the lossiness and left the silence, which is structural: a failed run is one red row nobody watches, `main` self-heals on the next merge and **erases the evidence**, and the lane's own verdict step writes to a job summary no one reads. Same shape as the nightly upgrade sweep that died reporting `failed=0`. **Decision: a separate reader compares what the lane ANNOUNCED (release commits) against what the registry HOLDS**; the difference is a phantom. The publisher infers from git because OIDC trusted publishing carries no read credential — **that never bound a reader**, and since task 1003872 the package is public, so an anonymous read needs none. Keeping witness and publisher separate IS the point. Daily not hourly (billable minutes, against a failure measured in hours), cadence pinned to the cron by `routine-schedule.js`; deliberately NOT on the session-start path (a third-party call behind a hook timeout). Healthy days are SILENT in Discord — findings to stdout, the all-clear to stderr — because a channel of reassurances stops being read. Two false-alarm sources, each guarded: a closed incident is permanent history (a 3-day window, by TIME not commit count, which a release burst would outrun), and **npm acknowledges a publish before its listing shows it** — found by RUNNING the check, which flagged 1.19.794 fifteen minutes after a publish whose log ended `published @bongos/core@1.19.794` and whose per-version endpoint answered 200; hence a 30-minute grace period plus per-version confirmation, with the listing only ever nominating suspects. A failed read throws rather than reading as "nothing missing". Proven by replaying the REAL outage from git + the live registry, not a synthetic failure. Does NOT detect a lane that stopped being triggered.](0295-the-registry-is-the-witness-the-lane-cannot-be.md) | release / observability |
390
390
  | 0296 | [**A role speciality is data over three existing surfaces, not a fourth mechanism** ([task 1003855](https://cloudbongos.com/builders#/task/1003855), goal 1000095). A craft says HOW you work; nothing said what you work ON. A speciality is a named bundle of knowledge, skills and an interaction contract sitting under a craft — an ideator with an Economics speciality. **Decision: it is DATA, not an ADR 0083 code module** — a subject-matter expert authors one, and requiring them to ship a package puts the mechanism out of reach of exactly the person it exists for. It layers over three surfaces that already exist (discipline-modes.json, the `behavior_prefs.interaction` contract path, the manifest contribution seam); only the knowledge bundle is new. The contract **rides the existing GET /me -> Conductor injection path rather than adding a second one**, and is CRAFT-SCOPED: the hook injects only the contract matching the claim in hand (which needed `t.discipline` on the active-claims read), and with no claim there is no craft and **nothing is injected** — silence beats guessing which expertise applies. Training is two-fold per the owner's 2026-09-17 ruling: documents they supplied, plus learnings captured from sessions worked in it (pointing at existing `learnings` rows, never a second corpus). The corpus is **inventoried, never inlined** — a document may run to 100k chars — so `speciality_documents` joins the recall ingester, because otherwise the contract's "search it with /recall" is a lie whose failure is silent. **`sellable` is recorded with no storefront behind it**: ADR 0107 defers the marketplace and explicitly rejected building one, so the flag captures the owner's decision when they make it and grants nothing — a deliberate partial implementation, recorded rather than silently made. Index scoping errs toward UNDER-sharing: only a public speciality's documents are corpus-wide, because `project` names a set the index cannot express.](0296-a-role-speciality-is-data-over-three-existing-surfaces.md) | roles / modules |
391
+ | 0297 | [**A task’s creator may retire it, and that is the ownership axis’s first route** ([task 1002443](https://cloudbongos.com/builders#/task/1002443), goal 1000095 — owner request 2026-07-25: *"the creator of that task should be able to abandon that task directly inside of the UI"*). Attribution itself shipped earlier and elsewhere (`core_227`, [task 1003278](https://cloudbongos.com/builders#/task/1003278)); what had not moved is that **retiring a task was a rank power and nothing else** — `task.abandon` floors at `metic` (ADR 0152), so a sub-Metic builder could file a task and then could not withdraw their own proposal. The model already had the vocabulary and had never used it: ADR 0151 §3 defines an **ownership axis** where a `scope:'own'` key authorizes nothing until a handler supplies the row's owner, R100 made `requirePermission` refuse such a key outright so `.own` can never degrade into `.any`, and it recorded that **zero call sites passed one**. This is that axis’s first real consumer. **Decision: two authorities, only one rank-shaped** — `task.abandon` (metic, any task) OR new `task.abandon.own` (xenos, a task you created, while it is still backlog/ready/blocked). Route becomes `requireBuilder` + an in-handler decision (the `POST /goals/:id/members` shape), pin `metic+archon`→`any-builder` so it now catches an accidental **tightening** as loudly as a gate removal. **The two facts resolve in two places on purpose:** the RANK fact is request-scoped and resolves before the transaction (uncached, ADR 0016); the OWNER fact is a column that can change under us, so `created_by` joins `abandonTask`’s existing `FOR UPDATE` SELECT and a new optional `authorize` callback decides under that lock. Reading `created_by` in the route would be a TOCTOU window; resolving permissions inside the transaction would hold a row lock across a second pool acquire. The decision is a PURE module (`abandon-authz.js`, the `goal-route-authz.js` precedent) so every branch is pinned DB-free and the hall hint and server verdict provably read one rule. **A creator’s window stops at the grader** — completed/confirmed stay cross-owner, because past the grader a retire is a ledger correction, not an author changing their mind; nothing is subtracted from `task.abandon`, pinned by a test. `counterpart: ['task.abandon']` is declared not derived, verb `act` not `manage` — `manage` would silently dominate every future narrower `task.*.own` key (the verb-blind escalation task 1003195 closed). **A null creator belongs to nobody and that is load-bearing:** ~1,357 pre-`core_227` tasks plus seed/restock/discord vectors are unattributed, so the owner comparison is the government resolver’s own — injected, never re-implemented, since a duplicated ownership comparison is how one quietly widens — and one slip would open every historical task to a xenos grant at once. Fail-closed throughout: a missing `government` port, a throwing resolver, and anything but an explicit `{ok:true}` all deny, and `authorize` runs BEFORE the idempotent already-abandoned return and the shipped/active-claim guards so an unauthorized caller cannot probe a row through a 200. No guard removed. Does NOT widen edit/promote/dependency (still Metic), does not touch `task.merge`, and does not backfill attribution — the control simply does not appear on tasks predating `core_227`.](0297-a-tasks-creator-may-retire-it.md) | authority / lifecycle / ownership |
@@ -16170,12 +16170,9 @@
16170
16170
  "tasks"
16171
16171
  ],
16172
16172
  "summary": "POST /tasks/:id/abandon",
16173
- "description": "POST /tasks/:id/abandon — terminal \"retire this task\" transition (task 987). The lifecycle had promote/ship/confirm/resolve but no first-class way to KILL a task that is no longer relevant: the only paths to status='abandoned' were offboarding a builder or a raw SQL UPDATE, so stale/moot tasks lingered in 'ready' as claimable noise. Refuses on a shipped task (immutable history) and on a task with an active claim (release it first); idempotent on an already- abandoned task. The abandon + its `reason` land in audit_log via the writing middleware, so the ledger keeps a record of who retired what. rank: metic+archon — scope removal; the inverse of /promote, and re-ranked to match it (ADR 0152, was archon under ADR 0090). A Metic who may author, shape, and promote work may also retire it: leaving the kill switch an Archon rung higher than the create switch only forced the owner to clean up after the triage they already delegated. Not one-way any more — /promote restores an abandoned task to backlog at the same rank — and the guards below (shipped refused, active claim refused) plus the audit_log entry are unchanged.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `task.abandon` (all required).",
16174
- "x-rank": "metic+archon",
16173
+ "description": "POST /tasks/:id/abandon — terminal \"retire this task\" transition (task 987). The lifecycle had promote/ship/confirm/resolve but no first-class way to KILL a task that is no longer relevant: the only paths to status='abandoned' were offboarding a builder or a raw SQL UPDATE, so stale/moot tasks lingered in 'ready' as claimable noise. Refuses on a shipped task (immutable history) and on a task with an active claim (release it first); idempotent on an already- abandoned task. The abandon + its `reason` land in audit_log via the writing middleware, so the ledger keeps a record of who retired what. rank: any-builder — DELIBERATELY, and the middleware list is not the whole gate (ADR 0297). Two authorities reach this transition: `task.abandon` (floor metic) retires ANY task, and `task.abandon.own` (floor xenos) retires one you CREATED while it is still backlog/ready/blocked. The second is a predicate over tasks.created_by, which no rank floor can express — the ADR 0151 ownership axis, and this is its first route. requirePermission structurally cannot carry it: an own-scoped key has no unconditional answer and that gate refuses one outright (auth.js, R100), so `.own` can never collapse into `.any`. The two facts are resolved in two places ON PURPOSE. The RANK fact is request-scoped and resolves here, once, uncached (ADR 0016); the OWNER fact is a column that can change under us, so it is evaluated inside abandonTask's FOR UPDATE transaction. Reading created_by here would be a TOCTOU window; resolving permissions in there would hold a row lock across a second pool acquire. Existing guards (shipped refused, active claim refused, idempotent re-abandon) and the audit_log entry are unchanged on both paths.\n\n**Rank:** `any-builder` — Any authenticated builder (row-level ownership enforced in-handler).",
16174
+ "x-rank": "any-builder",
16175
16175
  "x-source": "modules/lifecycle/routes/tasks.js",
16176
- "x-permissions": [
16177
- "task.abandon"
16178
- ],
16179
16176
  "parameters": [
16180
16177
  {
16181
16178
  "name": "id",
@@ -16187,23 +16184,12 @@
16187
16184
  "description": "Path parameter `id`."
16188
16185
  }
16189
16186
  ],
16190
- "requestBody": {
16191
- "required": false,
16192
- "content": {
16193
- "application/json": {
16194
- "schema": {
16195
- "$ref": "#/components/schemas/PostTasksIdAbandonRequest"
16196
- }
16197
- }
16198
- },
16199
- "x-validated": true
16200
- },
16201
16187
  "responses": {
16202
16188
  "200": {
16203
16189
  "description": "Success."
16204
16190
  },
16205
16191
  "400": {
16206
- "$ref": "#/components/responses/ValidationFailed"
16192
+ "$ref": "#/components/responses/BadRequest"
16207
16193
  },
16208
16194
  "401": {
16209
16195
  "$ref": "#/components/responses/Unauthorized"
@@ -16883,7 +16869,7 @@
16883
16869
  "tasks"
16884
16870
  ],
16885
16871
  "summary": "POST /tasks/:id/merge",
16886
- "description": "POST /tasks/:id/merge — mark a duplicate task merged into its canonical one (task 1001478 — the /bug-triage counterpart to idea_inbox's merge verdict). Implemented as `abandonTask` plus a target (db.mergeTask, db-ship.js) rather than a new `tasks.status` value — see that function's header for why. Refuses a self-merge, a shipped task, a task with an active claim, a target that doesn't exist, and a target that is itself merged elsewhere (no chains); idempotent when re-merging into the SAME target already recorded. rank: metic+archon — same floor as task.abandon, which this reuses under the hood; a Metic who may retire a task may also say which task it duplicates.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `task.merge` (all required).",
16872
+ "description": "**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `task.merge` (all required).",
16887
16873
  "x-rank": "metic+archon",
16888
16874
  "x-source": "modules/lifecycle/routes/tasks.js",
16889
16875
  "x-permissions": [
@@ -24021,15 +24007,6 @@
24021
24007
  "recommendation"
24022
24008
  ]
24023
24009
  },
24024
- "PostTasksIdAbandonRequest": {
24025
- "type": "object",
24026
- "properties": {
24027
- "reason": {
24028
- "type": "string"
24029
- }
24030
- },
24031
- "additionalProperties": false
24032
- },
24033
24010
  "PostTasksIdAttestGateRequest": {
24034
24011
  "type": "object",
24035
24012
  "properties": {
@@ -24783,7 +24760,7 @@
24783
24760
  }
24784
24761
  },
24785
24762
  "x-endpoint-count": 390,
24786
- "x-schema-count": 430,
24763
+ "x-schema-count": 429,
24787
24764
  "x-undocumented-bodies": 9,
24788
24765
  "x-response-schemas": 293,
24789
24766
  "x-generated-by": "scripts/gds/gen-api-docs.js"