@bongos/core 1.21.97 → 1.21.99
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 +56 -31
- package/docs/adr/0152-metic-task-abandonment.md +2 -0
- package/docs/adr/0371-auto-pickup-applies-a-submitted-tweak-on-the-runner.md +64 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +2 -2
- package/docs/architecture.md +3 -0
- package/docs/copy-inventory.md +32 -30
- package/docs/copy-registry.json +51 -33
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +10 -10
- package/migrations/core_274_parked_backlog_stays_parked.sql +140 -0
- package/modules/hall-ui/public/studio-lib.js +22 -0
- package/modules/hall-ui/public/studio.css +138 -7
- package/modules/hall-ui/public/studio.html +10 -4
- package/modules/hall-ui/public/studio.js +39 -1
- package/modules/hall-ui/records/studio.md +16 -0
- package/modules/lifecycle/db-ship.js +7 -2
- package/modules/lifecycle/db-tasks.js +25 -0
- package/modules/lifecycle/routes/tasks.js +5 -5
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +20 -0
- package/scripts/gds/government-walk.js +842 -0
- package/src/module-api.js +1 -1
- package/tests/abandon_task.mjs +1 -1
- package/tests/government_walk.mjs +56 -0
- package/tests/hall_studio_home.mjs +140 -5
- package/tests/parked_backlog_stays_parked.mjs +182 -0
- package/tests/promotion_triggers_skip_cascade.mjs +5 -3
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.21.
|
|
6
|
-
"core_contract": "1.21.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.21.99",
|
|
6
|
+
"core_contract": "1.21.99",
|
|
7
|
+
"source_commit": "262e039f7737aee04e24db9559e593544fab5532",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-
|
|
9
|
+
"built_at": "2026-10-09T12:05:36.533Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 591,
|
|
13
13
|
"agent_docs_stubbed": 28,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 3019,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 3639,
|
|
20
|
+
"tree_sha256": "6627907aa4a62c0516525dd3273bf188497dd8914388d4fcd5435d4563149bbf",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -1197,7 +1197,7 @@
|
|
|
1197
1197
|
{
|
|
1198
1198
|
"path": "docs/adr/0152-metic-task-abandonment.md",
|
|
1199
1199
|
"mode": "0000644",
|
|
1200
|
-
"sha256": "
|
|
1200
|
+
"sha256": "4fac5741dc3812f9c2229256fad097d5af627a9a00c471003145d7525f574568"
|
|
1201
1201
|
},
|
|
1202
1202
|
{
|
|
1203
1203
|
"path": "docs/adr/0153-sequential-chain-runner.md",
|
|
@@ -2309,10 +2309,15 @@
|
|
|
2309
2309
|
"mode": "0000644",
|
|
2310
2310
|
"sha256": "d4963a203bce58f5d5f422bab582e022bf2d2b33eaa854c0ee80b6b76ccff1ee"
|
|
2311
2311
|
},
|
|
2312
|
+
{
|
|
2313
|
+
"path": "docs/adr/0371-auto-pickup-applies-a-submitted-tweak-on-the-runner.md",
|
|
2314
|
+
"mode": "0000644",
|
|
2315
|
+
"sha256": "c23bc85e0e4e925911e7f7c3b2914b8e8dea8db725bd7c8d7840f4dade1f02a6"
|
|
2316
|
+
},
|
|
2312
2317
|
{
|
|
2313
2318
|
"path": "docs/adr/README.md",
|
|
2314
2319
|
"mode": "0000644",
|
|
2315
|
-
"sha256": "
|
|
2320
|
+
"sha256": "a010bc0f1d8a26126c5b86c24a4a0e548e038c651909e2b27d5fbdc7cbdd6f94"
|
|
2316
2321
|
},
|
|
2317
2322
|
{
|
|
2318
2323
|
"path": "docs/api-reference.md",
|
|
@@ -2322,12 +2327,12 @@
|
|
|
2322
2327
|
{
|
|
2323
2328
|
"path": "docs/api/openapi.json",
|
|
2324
2329
|
"mode": "0000644",
|
|
2325
|
-
"sha256": "
|
|
2330
|
+
"sha256": "e1042de87b32e2c0c4846a1718776d3f9397555c765c1bcbc4d3f0c25df08c9c"
|
|
2326
2331
|
},
|
|
2327
2332
|
{
|
|
2328
2333
|
"path": "docs/architecture.md",
|
|
2329
2334
|
"mode": "0000644",
|
|
2330
|
-
"sha256": "
|
|
2335
|
+
"sha256": "688331d1b50b20cca58d8a3addd72776f9531e5360635f1486f992b9793c0ec2"
|
|
2331
2336
|
},
|
|
2332
2337
|
{
|
|
2333
2338
|
"path": "docs/branding-contract.md",
|
|
@@ -2347,12 +2352,12 @@
|
|
|
2347
2352
|
{
|
|
2348
2353
|
"path": "docs/copy-inventory.md",
|
|
2349
2354
|
"mode": "0000644",
|
|
2350
|
-
"sha256": "
|
|
2355
|
+
"sha256": "1a099ad05b024aedba8a0e33212ade9859767aeb362e44d3110cb540471a3f20"
|
|
2351
2356
|
},
|
|
2352
2357
|
{
|
|
2353
2358
|
"path": "docs/copy-registry.json",
|
|
2354
2359
|
"mode": "0000644",
|
|
2355
|
-
"sha256": "
|
|
2360
|
+
"sha256": "ed419751a6b1f63b01bf9954707634a417f9cba613597d0e3310e3e38c18e45e"
|
|
2356
2361
|
},
|
|
2357
2362
|
{
|
|
2358
2363
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2862,7 +2867,7 @@
|
|
|
2862
2867
|
{
|
|
2863
2868
|
"path": "docs/module-api-changelog.md",
|
|
2864
2869
|
"mode": "0000644",
|
|
2865
|
-
"sha256": "
|
|
2870
|
+
"sha256": "d43b9ee9d1bae22277b8e9b9d425aafef3d1c2c5298814fd6a1a319e632917b7"
|
|
2866
2871
|
},
|
|
2867
2872
|
{
|
|
2868
2873
|
"path": "docs/modules-contract.md",
|
|
@@ -2952,7 +2957,7 @@
|
|
|
2952
2957
|
{
|
|
2953
2958
|
"path": "docs/page-readings.json",
|
|
2954
2959
|
"mode": "0000644",
|
|
2955
|
-
"sha256": "
|
|
2960
|
+
"sha256": "64bfd69c24b6f0a1c99b5cea536b6730c0d1a212d71f97e9e9fe4fe07322e9dd"
|
|
2956
2961
|
},
|
|
2957
2962
|
{
|
|
2958
2963
|
"path": "docs/project-context.template.md",
|
|
@@ -4084,6 +4089,11 @@
|
|
|
4084
4089
|
"mode": "0000644",
|
|
4085
4090
|
"sha256": "7a1348d5d881859db66a0bacd9e2956d8b16a8ddcaf4f65e91408508a6c8d35b"
|
|
4086
4091
|
},
|
|
4092
|
+
{
|
|
4093
|
+
"path": "migrations/core_274_parked_backlog_stays_parked.sql",
|
|
4094
|
+
"mode": "0000644",
|
|
4095
|
+
"sha256": "48266ddb9168d1240029ecf6e69572010512dddc219d0f0cb4d6b373775752eb"
|
|
4096
|
+
},
|
|
4087
4097
|
{
|
|
4088
4098
|
"path": "modules/agents/CLAUDE.md",
|
|
4089
4099
|
"mode": "0000644",
|
|
@@ -6307,22 +6317,22 @@
|
|
|
6307
6317
|
{
|
|
6308
6318
|
"path": "modules/hall-ui/public/studio-lib.js",
|
|
6309
6319
|
"mode": "0000644",
|
|
6310
|
-
"sha256": "
|
|
6320
|
+
"sha256": "13510574f86779136d6c05d12739974cb84090ee80e1c494c83c20745ec3baeb"
|
|
6311
6321
|
},
|
|
6312
6322
|
{
|
|
6313
6323
|
"path": "modules/hall-ui/public/studio.css",
|
|
6314
6324
|
"mode": "0000644",
|
|
6315
|
-
"sha256": "
|
|
6325
|
+
"sha256": "9a3b85b8760200225d26fa062b8c95470f7b6902889a51a422c7f4787d73bc80"
|
|
6316
6326
|
},
|
|
6317
6327
|
{
|
|
6318
6328
|
"path": "modules/hall-ui/public/studio.html",
|
|
6319
6329
|
"mode": "0000644",
|
|
6320
|
-
"sha256": "
|
|
6330
|
+
"sha256": "7c7053c61fd30cd79fd131f25058f1cf169c9d8610427664010dc09fb08f2fba"
|
|
6321
6331
|
},
|
|
6322
6332
|
{
|
|
6323
6333
|
"path": "modules/hall-ui/public/studio.js",
|
|
6324
6334
|
"mode": "0000644",
|
|
6325
|
-
"sha256": "
|
|
6335
|
+
"sha256": "f3a9fa920f732bddf245148c4faff10a52e88724b33800ae440b37e9aa1cd1da"
|
|
6326
6336
|
},
|
|
6327
6337
|
{
|
|
6328
6338
|
"path": "modules/hall-ui/public/studio.states.json",
|
|
@@ -6582,7 +6592,7 @@
|
|
|
6582
6592
|
{
|
|
6583
6593
|
"path": "modules/hall-ui/records/studio.md",
|
|
6584
6594
|
"mode": "0000644",
|
|
6585
|
-
"sha256": "
|
|
6595
|
+
"sha256": "794b41537bbae12a111bfe69ff856653a14154827cfc7039ccf05a1231504174"
|
|
6586
6596
|
},
|
|
6587
6597
|
{
|
|
6588
6598
|
"path": "modules/hall-ui/records/tweak-editor.md",
|
|
@@ -6927,12 +6937,12 @@
|
|
|
6927
6937
|
{
|
|
6928
6938
|
"path": "modules/lifecycle/db-ship.js",
|
|
6929
6939
|
"mode": "0000644",
|
|
6930
|
-
"sha256": "
|
|
6940
|
+
"sha256": "982d65a4e74063ba914a48ace9e95b595bd1540ea27b1c9f47a842c82f8c9ff6"
|
|
6931
6941
|
},
|
|
6932
6942
|
{
|
|
6933
6943
|
"path": "modules/lifecycle/db-tasks.js",
|
|
6934
6944
|
"mode": "0000644",
|
|
6935
|
-
"sha256": "
|
|
6945
|
+
"sha256": "710e4c97c0b67a3b258d6997ba9dc485e42bb57a0988777c0f10f8391b952489"
|
|
6936
6946
|
},
|
|
6937
6947
|
{
|
|
6938
6948
|
"path": "modules/lifecycle/db-versions.js",
|
|
@@ -7387,7 +7397,7 @@
|
|
|
7387
7397
|
{
|
|
7388
7398
|
"path": "modules/lifecycle/routes/tasks.js",
|
|
7389
7399
|
"mode": "0000644",
|
|
7390
|
-
"sha256": "
|
|
7400
|
+
"sha256": "1ced2b26ca9ce2e755e7ac1ce5b56130785041119a9373e3290250ee4b790ff4"
|
|
7391
7401
|
},
|
|
7392
7402
|
{
|
|
7393
7403
|
"path": "modules/lifecycle/routes/version-route-authz.js",
|
|
@@ -10117,12 +10127,12 @@
|
|
|
10117
10127
|
{
|
|
10118
10128
|
"path": "package-lock.json",
|
|
10119
10129
|
"mode": "0000644",
|
|
10120
|
-
"sha256": "
|
|
10130
|
+
"sha256": "743e74b9cb354d11b19bb40b81f9640932ac68730487816569ee5796875392a4"
|
|
10121
10131
|
},
|
|
10122
10132
|
{
|
|
10123
10133
|
"path": "package.json",
|
|
10124
10134
|
"mode": "0000644",
|
|
10125
|
-
"sha256": "
|
|
10135
|
+
"sha256": "e74953a3c03fb015d7f54229580414d430cff7159d5361dca7754eb28d852310"
|
|
10126
10136
|
},
|
|
10127
10137
|
{
|
|
10128
10138
|
"path": "public-docs/index.html",
|
|
@@ -10142,7 +10152,7 @@
|
|
|
10142
10152
|
{
|
|
10143
10153
|
"path": "release-notes.json",
|
|
10144
10154
|
"mode": "0000644",
|
|
10145
|
-
"sha256": "
|
|
10155
|
+
"sha256": "76a1303dec3ba5b72f9a7eb03de1896d6dafc040c8c9260af6c7b8fe2a6e58cf"
|
|
10146
10156
|
},
|
|
10147
10157
|
{
|
|
10148
10158
|
"path": "scripts/cron/style_guide_audit.sh",
|
|
@@ -10824,6 +10834,11 @@
|
|
|
10824
10834
|
"mode": "0000644",
|
|
10825
10835
|
"sha256": "320841b1e4df014966ea9b6b70645ecb7915b412f94b8fa0d9835dae5b9446c5"
|
|
10826
10836
|
},
|
|
10837
|
+
{
|
|
10838
|
+
"path": "scripts/gds/government-walk.js",
|
|
10839
|
+
"mode": "0000644",
|
|
10840
|
+
"sha256": "f7a6091ce0621fdfdf69f667e44e52aaf96f6a1222f52f9e5730ff003e162383"
|
|
10841
|
+
},
|
|
10827
10842
|
{
|
|
10828
10843
|
"path": "scripts/gds/grade-correlation-audit.js",
|
|
10829
10844
|
"mode": "0000644",
|
|
@@ -12412,7 +12427,7 @@
|
|
|
12412
12427
|
{
|
|
12413
12428
|
"path": "src/module-api.js",
|
|
12414
12429
|
"mode": "0000644",
|
|
12415
|
-
"sha256": "
|
|
12430
|
+
"sha256": "fe5b5772fd9a33f1aa10b93689e32179c6c81d9dbf9a3a33ee4af2383a3bdb02"
|
|
12416
12431
|
},
|
|
12417
12432
|
{
|
|
12418
12433
|
"path": "src/module-loader/catalog.js",
|
|
@@ -12487,7 +12502,7 @@
|
|
|
12487
12502
|
{
|
|
12488
12503
|
"path": "tests/abandon_task.mjs",
|
|
12489
12504
|
"mode": "0000644",
|
|
12490
|
-
"sha256": "
|
|
12505
|
+
"sha256": "598f0ee6d3634dc23aeb391d2dc9644dc6a0e8287d47de7d6374cceebe9c96f3"
|
|
12491
12506
|
},
|
|
12492
12507
|
{
|
|
12493
12508
|
"path": "tests/access_requests_invite.mjs",
|
|
@@ -14549,6 +14564,11 @@
|
|
|
14549
14564
|
"mode": "0000644",
|
|
14550
14565
|
"sha256": "444f0638dccebc42e5a371820ed565c665d4373c198e1581389ab222bf267c5e"
|
|
14551
14566
|
},
|
|
14567
|
+
{
|
|
14568
|
+
"path": "tests/government_walk.mjs",
|
|
14569
|
+
"mode": "0000644",
|
|
14570
|
+
"sha256": "35d39431886fb2faad9508dd8ca481987760432397464b1b702aef0451de2270"
|
|
14571
|
+
},
|
|
14552
14572
|
{
|
|
14553
14573
|
"path": "tests/governor_docket.mjs",
|
|
14554
14574
|
"mode": "0000644",
|
|
@@ -14927,7 +14947,7 @@
|
|
|
14927
14947
|
{
|
|
14928
14948
|
"path": "tests/hall_studio_home.mjs",
|
|
14929
14949
|
"mode": "0000644",
|
|
14930
|
-
"sha256": "
|
|
14950
|
+
"sha256": "0079447c51ccac1d4e25ca488c7b7df11a8aa79bce7f82a901ad6b10710fc38f"
|
|
14931
14951
|
},
|
|
14932
14952
|
{
|
|
14933
14953
|
"path": "tests/hall_studio_world.mjs",
|
|
@@ -16129,6 +16149,11 @@
|
|
|
16129
16149
|
"mode": "0000644",
|
|
16130
16150
|
"sha256": "2f9996efb0ed59579e000cd105a0b461dbcf80f5f08def5c4535a849b16bc963"
|
|
16131
16151
|
},
|
|
16152
|
+
{
|
|
16153
|
+
"path": "tests/parked_backlog_stays_parked.mjs",
|
|
16154
|
+
"mode": "0000644",
|
|
16155
|
+
"sha256": "f87af6b6768e29d523a7b47f63f1a4940037f8ab8bbfe224570ccb03af9cee03"
|
|
16156
|
+
},
|
|
16132
16157
|
{
|
|
16133
16158
|
"path": "tests/parse_cookie_decode.mjs",
|
|
16134
16159
|
"mode": "0000644",
|
|
@@ -16472,7 +16497,7 @@
|
|
|
16472
16497
|
{
|
|
16473
16498
|
"path": "tests/promotion_triggers_skip_cascade.mjs",
|
|
16474
16499
|
"mode": "0000644",
|
|
16475
|
-
"sha256": "
|
|
16500
|
+
"sha256": "acb8c2f7f2bc100e9bb637035c69a443e942d8623872cb4d51edccd2b804c6e6"
|
|
16476
16501
|
},
|
|
16477
16502
|
{
|
|
16478
16503
|
"path": "tests/prose_edits.mjs",
|
|
@@ -37,6 +37,8 @@ That was tolerable while abandon *was* Archon-only (the person who could break i
|
|
|
37
37
|
|
|
38
38
|
Restore lands in `backlog`, never straight to `ready`. The task was retired deliberately; bringing it back should re-enter it as un-workable and require a second, explicit promote to become claimable. The restore branch returns before the reward gate ([ADR 0096](0096-require-reward-before-workable.md)) — that is a promote-to-*workable* concern, and `backlog` is the pre-workable state, the same reasoning `/demote` already uses.
|
|
39
39
|
|
|
40
|
+
**Amendment (task 1004592, migration `core_274`).** Landing in `backlog` was not enough on its own: the promotion sweep (`promoteUnblockedBacklogTasks`) drains every backlog row with no unmet dependency and no open blocker, so a restored task was promoted on the next reconciler tick and the "second, explicit promote" never had to happen (observed on task 1003397, 2026-10-06). A restore — and `POST /tasks/:id/demote` — now stamps `tasks.parked_at` / `parked_by`. The sweep and the three promotion triggers skip a parked row, and a trigger clears the stamp whenever the task leaves `backlog`, so a person's promote is the only way out. A task filed straight into backlog by hand is not parked; that intent is not recorded at create time.
|
|
41
|
+
|
|
40
42
|
The backing `db.restoreTask` is the mirror of `db.abandonTask`: `SELECT … FOR UPDATE`, guard, single `UPDATE`, commit, with `TASK_NOT_FOUND` / `TASK_NOT_ABANDONED` as its refusals. It clears the `value_summary = 'Abandoned: <reason>'` stamp abandon writes — and *only* that stamp (`CASE WHEN value_summary LIKE 'Abandoned: %'`) — so a restored task never carries a retire note into a later ship card or `#ship-news` broadcast, while an unrelated summary survives.
|
|
41
43
|
|
|
42
44
|
### What is unchanged
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# ADR 0371 — Auto-pickup applies a submitted tweak on the runner, and the artist still approves
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-10-09
|
|
5
|
+
- **Task:** [task 1004330](https://cloudbongos.com/builders#/task/1004330) (BV2.TW19, goal 1000095, BONGOS-V2)
|
|
6
|
+
- **Builds on:** [ADR 0341](0341-the-page-is-the-unit-of-tweak-mode.md) (Tweak Mode; D7 nothing ships without the artist's approval), [ADR 0307](0307-autobongos-a-deterministic-supervisor-and-agentic-workers.md) (Autobongos: one supervisor, one headless worker per task, a DB fence), [ADR 0369](0369-ship-mode-decides-who-starts-a-ship-not-how-it-lands.md) (a live knob in `project_settings` behind one resolver)
|
|
7
|
+
- **Decided with:** the owner, 2026-09-27 ("queue now, auto later") and 2026-10-08 (write the design now, without waiting for the live artist proof, task 1004329)
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
In Tweak Mode an artist rewrites a whole page in the studio and presses submit. The round then waits, at `ready` with no claim, until a builder or agent session runs `/tweak` (`modules/copy-desk/skills/tweak/SKILL.md`). That session claims the round, applies the words with `copy-apply.js --batch`, takes before and after renders, commits and runs `ship.js`. A passed grade is then held for the artist (`page-tweak-hold.js`), who approves it in the Approval queue or sends it back.
|
|
12
|
+
|
|
13
|
+
So an artist with nobody at a terminal waits indefinitely. The task asks for an agent that picks up a submitted page on its own, and names four questions: the trigger, who pays, the loop guard, and the rank it runs at.
|
|
14
|
+
|
|
15
|
+
Two machines in the core could host it:
|
|
16
|
+
|
|
17
|
+
- **The agents module** (`modules/agents/`). It fires on events, from the web server, through `claude -p --tools Read`. That spawn lives in grading (`modules/grading/grader-subagent.js`) and is hardcoded read-only, which is why its agents "may only report". Applying a tweak needs file writes, git, a browser for the page reader and renders, and `ship.js`. Widening the grader's spawn is an edit to a protected path, and the web server process has no worktree to write in.
|
|
18
|
+
- **Autobongos** (ADR 0307, `scripts/gds/autobongos-run.js`). A supervisor on a builder's machine claims a task, starts one headless worker in a worktree, and reads the outcome from the ledger. It already writes code, ships through the normal grade, and is bounded by a DB fence (kill switch, goal allowlist, protected paths, a grader-outage stop), a wall clock and a grade cap.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
**D1 — Auto-pickup is a lane of the Autobongos runner, not an agent.** The runner gains a *tweak lane*. Before it picks ordinary work, it asks for the oldest submitted round and, if the lane is allowed, hands that one page to a worker that runs `/tweak` for it. Everything a person's `/tweak` session does still happens, in the same order, through the same scripts. The worker gains no new power: it runs as the runner's builder, through the claim, grade and land paths every session uses. The agents module stays read-only.
|
|
23
|
+
|
|
24
|
+
**D2 — The trigger is the runner's own poll, not a new event.** Submit emits nothing today, and nothing needs it to: the runner already wakes on its cadence and reads the ledger. The lane reads the same derived state the studio shows (`open_round.state = 'submitted'` from `modules/copy-desk/page-status.js`), oldest first, so the queue order an artist sees is the order it is worked. A round becomes eligible the moment it is submitted; how soon it is picked up is the runner's cadence, which is stated on the studio rather than promised.
|
|
25
|
+
|
|
26
|
+
**D3 — It is off until the project turns it on.** A new project setting, `tweak.autopickup` (`off` | `on`, default `off`), in `project_settings` behind one resolver, as ADR 0369 does for `ship.mode`. Any builder can read it, `project.settings.manage` writes it. The lane also needs everything an Autobongos run already needs: the fence's kill switch open, a grader that is up, and a runner some Metic+ builder has chosen to run. The setting replaces only the **goal allowlist** for this lane, because a tweak round lives in whatever goal holds the version's page work and an artist cannot be expected to know which goal to allowlist. It does not replace any other fence rule.
|
|
27
|
+
|
|
28
|
+
**D4 — It runs at the rank of the builder whose runner it is, and that must be Metic or higher.** There is no new service principal. `/tweak` is Metic+ already (`copy-apply.js` writes a `page-tweak-applied` block, which needs `task.edit`), so a runner below Metic gets the same refusal a person would, and the lane reports it once and stops trying. A bot identity was rejected: `builders.system_role` principals are trusted for identity and never for authorization ([ADR 0033](0033-discord-bot-service-principal.md)), and a new one would be a second way to hold `task.edit` that no person holds.
|
|
29
|
+
|
|
30
|
+
**D5 — Who pays.** The worker's model time is the runner owner's own capacity, exactly as for any Autobongos task: the gauge keeps the owner's reserve and the wall clock (`DEFAULT_WORKER_TIMEOUT_MS`) bounds one worker. The grade is charged to the project, as every grade is. Because a tweak costs the project a grade without a person choosing to spend it, the lane has its own ceiling: **`tweak.autopickup_monthly_cap`**, a count of rounds the lane may pick up per calendar month (default **20**), counted from the ledger, never from a counter that can drift. The lane claims with an `auto_pickup` mark that the server records on the claim row, and **the server enforces the cap at claim time**: the count and the claim insert run in one transaction under a project-wide advisory lock (`page-tweak/autopickup`), so two runners racing for the last slot cannot both get it. At the cap the claim is refused, the lane stops and the studio says so; rounds wait for a person, as today. The mark is a cost brake on a runner the owner chose to run, not a security boundary: a worker that claimed without it would be claiming as its builder, which that builder may already do by hand. **Why 20:** a grade costs the project a few dollars (the `/tweak` skill's own cost line), so 20 rounds is a bounded, legible monthly spend at about the level of one active artist; the owner can raise it. The cost-plus session reward (ADR 0054) is unchanged: it goes to the builder whose session shipped, as for any worktree claim, and the artist's page credit is untouched (ADR 0341 D10).
|
|
31
|
+
|
|
32
|
+
**D6 — The loop guard is per round, and the artist is the gate that matters.**
|
|
33
|
+
1. **Nothing lands without the artist.** The held pass (`isHeldForArtist`) is unchanged, so the worst an unattended worker can do is offer the artist a bad render to send back. This is why auto-pickup is safe to have at all.
|
|
34
|
+
2. **Two automatic applies per round, then a person.** A sent-back round returns to `ready` with the artist's note, and the lane may take it again, because the note is the artist's instruction. After the lane has applied a round twice (counted from that round's own claims by the runner), it leaves the round for a person and the studio shows "waiting for a person". The server refuses a third `auto_pickup` claim on the same round, with the same refusal shape as the cap. **Why two:** one apply plus one try at the artist's note; a round sent back twice is a disagreement a person should read, not a third render to approve.
|
|
35
|
+
3. **A failed apply is not retried until the round changes.** `copy-apply.js` exit 2 (nothing applied) or a refused claim releases the round with a note, and the lane skips it until the artist edits or resubmits it (its `updated_at` moves). The runner's existing set-aside and grade cap (one grade plus one re-grade) still apply on top.
|
|
36
|
+
|
|
37
|
+
**D7 — The fence's other rules are kept, on the page's files.** A round's touches are the page's files from the page inventory. The [ADR 0043](0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md) protected-path matcher and the host-access check run over those exactly as `decideTask` runs them over a task's touches. A page whose files hit a protected path (the shared shell is the likely one) is left for a person, and the studio says why. The lane never edits a file outside the page's own list; a rewrite that needs one is already its own engineer task (`page-tweak-unplaced`).
|
|
38
|
+
|
|
39
|
+
**D8 — The worker is headless, so `/tweak` gains a non-interactive mode.** Today the skill can stop and ask. The worker cannot. `/tweak --page <page-id> --unattended` takes one named round, never asks, and on any halt (claim refused, nothing applied, ship-check stale, grade unavailable) releases the round with a plain note and exits. It never runs `--skip-grade`, never edits a protected path, and never ships more than the one round it was given.
|
|
40
|
+
|
|
41
|
+
**Those rules are not left to the worker.** The skill states them; the server holds them, so a worker that ignored its instructions gets refused rather than trusted:
|
|
42
|
+
- **No land without the artist.** A page-tweak round reaches `confirmed` only through the artist's approval (`approveHeldTweak`) or the owner's override request, never through a ship path that skips or bypasses the grade. Task 1004678 pins this with a test over every confirm path, and closes any path that does not hold it.
|
|
43
|
+
- **No file outside the page.** At publish, the server reads the merged change's files itself (`ship-files.js`, not the client's report) and refuses an `auto_pickup` round whose diff touches any file outside that page's inventory list, or a protected path.
|
|
44
|
+
- **One round per claim** is the claim model already: a worktree holds one active claim.
|
|
45
|
+
|
|
46
|
+
**D9 — The artist can see it happened.** The Approval queue already names who applied a round ("with help from", ADR 0341 / task 1004432). An automatic apply is marked "applied automatically" there and in the studio's queue line, and the project settings page carries the switch and the month's count against the cap.
|
|
47
|
+
|
|
48
|
+
## Rejected
|
|
49
|
+
|
|
50
|
+
- **A write-capable agent in `modules/agents/`.** It would need a capability-parametrized spawn in grading (a protected edit) and a worktree, a browser and git inside the web server process. Every guard Autobongos already has would be rebuilt there.
|
|
51
|
+
- **A `page.submitted` event.** Useful only to a host that sleeps between events. The runner polls the ledger, which cannot miss a submit that happened while it was down; an event can.
|
|
52
|
+
- **Landing without the artist when the grade passes.** Contradicts ADR 0341 D7, and it is the artist's approval that makes an unattended apply acceptable.
|
|
53
|
+
- **A per-round dollar cap.** The grade cost is set by the grader, not by the lane. A count of rounds is something the owner can reason about and the ledger can count exactly.
|
|
54
|
+
|
|
55
|
+
## Consequences
|
|
56
|
+
|
|
57
|
+
- Built by five tasks in goal 1000095:
|
|
58
|
+
- [task 1004677](https://cloudbongos.com/builders#/task/1004677) (TW19a): the setting, the claim mark and the server-enforced cap and per-round limit (D3, D5, D6; needs a migration);
|
|
59
|
+
- [task 1004678](https://cloudbongos.com/builders#/task/1004678) (TW19b): `/tweak --page <id> --unattended` and the server guards under it, after TW19a (D8);
|
|
60
|
+
- [task 1004679](https://cloudbongos.com/builders#/task/1004679) (TW19c): the runner's tweak lane, after the two above (D1, D2, D4, D6, D7);
|
|
61
|
+
- [task 1004680](https://cloudbongos.com/builders#/task/1004680) (TW19d): the studio and settings surfaces (D9);
|
|
62
|
+
- [task 1004681](https://cloudbongos.com/builders#/task/1004681) (TW19e): the live proof, which also waits for the artist proof ([task 1004329](https://cloudbongos.com/builders#/task/1004329)) because it needs a real artist to approve the result.
|
|
63
|
+
- Nothing changes until a project turns `tweak.autopickup` on and a Metic+ builder runs the runner. A project that never does keeps today's queue exactly.
|
|
64
|
+
- An instance with no runner has no auto-pickup. That is deliberate: the core does not run unattended workers in its own server process.
|
package/docs/adr/README.md
CHANGED
|
@@ -489,3 +489,4 @@ These 20 numbers are each shared by exactly two files. They are **accepted histo
|
|
|
489
489
|
| 0368 | [**Red and blue teams practise on a range, never on live** (goal 1000094 — *Working area 10, Security*; proposed). **D2:** the range is its own small server, rebuilt from the candidate the owner marks "stage for attack". **D3:** it shares no identity hub, secret or data with live. **D5:** red is AI plus Metic+, with one side at a time and a 30-day switch. **D6:** attacks run on subscriptions, never the metered API. **D7:** 3 runs on the newest model with 0 high+ breaks, advisory until an outside tenant exists. **D8:** points only, no credits yet. **D9:** three live signals to `#security-alerts`.](0368-red-and-blue-teams-practice-on-a-range-never-on-live.md) | security / red-team / range |
|
|
490
490
|
| 0369 | [**Ship mode decides who starts a ship, not how it lands** ([task 1004628](https://cloudbongos.com/builders#/task/1004628), goal 1000106). **D1:** a `ship.mode` project setting, `auto` (the AI ships and lands a finished task with no commands) or `manual` (a person approves each ship), in `project_settings` with no migration. **D2:** Auto is the default, and the resolver reports whether anyone has chosen. **D3:** it is separate from deploy mode (`config/deploy.json`), which decides how a land happens. **D4:** any builder can read it (the AI must know), and `project.settings.manage` writes it, from the settings page or `bongos ship-mode`.](0369-ship-mode-decides-who-starts-a-ship-not-how-it-lands.md) | lifecycle / shipping / project settings |
|
|
491
491
|
| 0370 | [**A repo we create for a new project is private** ([task 1004645](https://cloudbongos.com/builders#/task/1004645), goal 1000106). Supersedes ADR 0155 Decision 3: `createUserRepo` sends `private: true`, still not a parameter, and the wizard says the new repo is private. Safe because ADR 0176 already gives a private repo its own read-only deploy key before the first pull; the owner can make it public from the manage page.](0370-a-repo-we-create-is-private.md) | onboarding / security / github |
|
|
492
|
+
| 0371 | [**Auto-pickup applies a submitted tweak on the runner, and the artist still approves** ([task 1004330](https://cloudbongos.com/builders#/task/1004330), goal 1000095, BV2.TW19). **D1:** a tweak lane of the Autobongos runner (ADR 0307), not a write-capable agent: the agents spawn is read-only by construction. **D2:** the trigger is the runner's ledger poll of the oldest `submitted` round, no new event. **D3:** off until a `tweak.autopickup` project setting turns it on; it replaces only the fence's goal allowlist. **D4:** runs as the runner's Metic+ builder, no service principal. **D5:** model time is the runner owner's; grades are the project's, under a monthly round cap (default 20) counted from the ledger. **D6:** the held pass still waits for the artist; two automatic applies per round, then a person; a failed apply waits for the round to change. **D7:** protected-path and host checks run on the page's files. **D8:** `/tweak --unattended`. **D9:** "applied automatically" is shown. Build tasks 1004677–1004681.](0371-auto-pickup-applies-a-submitted-tweak-on-the-runner.md) | copy-desk / autonomy / tweak mode |
|
package/docs/api/openapi.json
CHANGED
|
@@ -23560,7 +23560,7 @@
|
|
|
23560
23560
|
"tasks"
|
|
23561
23561
|
],
|
|
23562
23562
|
"summary": "POST /tasks/:id/demote",
|
|
23563
|
-
"description": "POST /tasks/:id/demote — the strict inverse of /promote: flip a task ready→backlog and nothing else. Use case (task 1223): a dependency-gated task that was wrongly promoted to ready needs to go back to backlog without hand-SQL on prod. Mirrors /promote's auth (Metic+ — scope mutation, ADR 0090). Strictly ready→backlog: refuse any other current status (blocked, active, or any later lifecycle state). A claimed task is `active`, so this gate also refuses demoting work that's already in flight — demote is not a way to yank a task out from under its claim holder. No reward side-effects (the reward gate is a promote-only concern
|
|
23563
|
+
"description": "POST /tasks/:id/demote — the strict inverse of /promote: flip a task ready→backlog and nothing else. Use case (task 1223): a dependency-gated task that was wrongly promoted to ready needs to go back to backlog without hand-SQL on prod. Mirrors /promote's auth (Metic+ — scope mutation, ADR 0090). Strictly ready→backlog: refuse any other current status (blocked, active, or any later lifecycle state). A claimed task is `active`, so this gate also refuses demoting work that's already in flight — demote is not a way to yank a task out from under its claim holder. No reward side-effects (the reward gate is a promote-only concern). The task lands PARKED so the sweep leaves it (1004592). rank: metic+archon — ready→backlog demotion; scope mutation; mirrors /promote (ADR 0090).\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `task.demote` (all required).",
|
|
23564
23564
|
"x-rank": "metic+archon",
|
|
23565
23565
|
"x-source": "modules/lifecycle/routes/tasks.js",
|
|
23566
23566
|
"x-permissions": [
|
|
@@ -24037,7 +24037,7 @@
|
|
|
24037
24037
|
"tasks"
|
|
24038
24038
|
],
|
|
24039
24039
|
"summary": "POST /tasks/:id/promote",
|
|
24040
|
-
"description": "backlog→ready promotion; scope mutation; ADR 0090 (was archon). Also the UN-ABANDON path (ADR 0152): 'abandoned' → 'backlog'. When abandon moved down to Metic+ it needed an undo at the same rank, or a Metic's mis-retire would be unrecoverable without Archon SQL (no other route restores an abandoned task — PATCH refuses `status` outright). A restore lands in **backlog**, never straight to ready: the task was retired for a reason, so it re-enters
|
|
24040
|
+
"description": "backlog→ready promotion; scope mutation; ADR 0090 (was archon). Also the UN-ABANDON path (ADR 0152): 'abandoned' → 'backlog'. When abandon moved down to Metic+ it needed an undo at the same rank, or a Metic's mis-retire would be unrecoverable without Archon SQL (no other route restores an abandoned task — PATCH refuses `status` outright). A restore lands in **backlog**, never straight to ready: the task was retired for a reason, so it re-enters PARKED (core_274, task 1004592 — the promotion sweep skips it) and only a second promote frees it.\n\n**Rank:** `metic+archon` — Metic or Archon rank (review/triage powers).\n\n**Permissions:** `task.promote` (all required).",
|
|
24041
24041
|
"x-rank": "metic+archon",
|
|
24042
24042
|
"x-source": "modules/lifecycle/routes/tasks.js",
|
|
24043
24043
|
"x-permissions": [
|
package/docs/architecture.md
CHANGED
|
@@ -108,6 +108,9 @@ versions(id PK 'V1'|'PMS-V1'..., name, track, status, scope_doc_path, ...) -- t
|
|
|
108
108
|
tasks(id, version_id, title, description, status, touches text[], est_minutes, est_cost_usd,
|
|
109
109
|
manual_degree, priority, automation_tag, credits_reward, source, source_ref,
|
|
110
110
|
promoted_at, shipped_at, shipped_by, value_summary, created_at, updated_at)
|
|
111
|
+
+ parked_at, parked_by (mig core_274, task 1004592) -- a PERSON put this task in backlog (restore
|
|
112
|
+
or /demote); the promotion sweep and the three promotion triggers skip it, and a trigger clears
|
|
113
|
+
both whenever status leaves 'backlog'
|
|
111
114
|
+ visual_url, visual_alt (mig core_223, task 1003109) -- the OPTIONAL ship-time VISUAL: a
|
|
112
115
|
screenshot / before-after / diagram, stored on disk under <INSTANCE ROOT>/var/task-visuals/
|
|
113
116
|
(never inside the installed core package, which every upgrade replaces; task 1004381) and served by
|