@bongos/core 1.19.647 → 1.19.649

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.647",
6
- "core_contract": "1.19.647",
7
- "source_commit": "8fa0f8d28d7d61f52297185e5c669d70a60480de",
5
+ "core_version": "1.19.649",
6
+ "core_contract": "1.19.649",
7
+ "source_commit": "10ae5631783d03386a93457958ac7846e3ad4b83",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-10T18:40:35.458Z",
9
+ "built_at": "2026-09-10T19:54:28.577Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 470,
13
13
  "agent_docs_stubbed": 24,
14
- "functional_verbatim": 2107,
14
+ "functional_verbatim": 2109,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2601,
20
- "tree_sha256": "7cebbe4108e45c224a38f2068e1e7a1d00552500b0b6f6d7f5c111d45f63a037",
19
+ "file_count": 2603,
20
+ "tree_sha256": "05f4d97454a0e6f6d8eadf6c13d88c68d8619b32269b20409e5a79505dad4596",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/backlog-review/SKILL.md",
@@ -382,7 +382,7 @@
382
382
  {
383
383
  "path": "clients/bongos-client/index.d.ts",
384
384
  "mode": "0000644",
385
- "sha256": "a6b15120b937f2a73d47ef180956068560eb3ac3efe5cf58f3a021b544ff9bc0"
385
+ "sha256": "5ef1befd1a1b30e8ef9e8b8ca08f768361d2422f6ddba2160a5395d46a84d443"
386
386
  },
387
387
  {
388
388
  "path": "clients/bongos-client/index.mjs",
@@ -1892,12 +1892,12 @@
1892
1892
  {
1893
1893
  "path": "docs/api-reference.md",
1894
1894
  "mode": "0000644",
1895
- "sha256": "0fa07d5f61ab66a43e0c51e51cfac8223afcb1aa12ea7342a226cb1832c0be62"
1895
+ "sha256": "6c5f26c9c00387ecc84ed80726a4d62eca0f017f1d75c304d758961749fe616f"
1896
1896
  },
1897
1897
  {
1898
1898
  "path": "docs/api/openapi.json",
1899
1899
  "mode": "0000644",
1900
- "sha256": "02c082bff4c5bebbf13cefeeb46a6b9cdff430cc32b65dd4e78a72248c6fd74f"
1900
+ "sha256": "d63a4ab6e92a24d339a632360fb92246512e09eabbc5bff632881bda6d4abf50"
1901
1901
  },
1902
1902
  {
1903
1903
  "path": "docs/architecture.md",
@@ -2772,7 +2772,7 @@
2772
2772
  {
2773
2773
  "path": "docs/module-api-changelog.md",
2774
2774
  "mode": "0000644",
2775
- "sha256": "0f1d4f5ea01f97e24ee83708c35267828f6d7fb2c64a47db9b7b94c43205951a"
2775
+ "sha256": "454e5258c7dccd1013445f290acf0cfbd0cf396c9cb5bbcf8a08b911770bfa07"
2776
2776
  },
2777
2777
  {
2778
2778
  "path": "docs/modules-contract.md",
@@ -5662,7 +5662,7 @@
5662
5662
  {
5663
5663
  "path": "modules/lifecycle/db-tasks.js",
5664
5664
  "mode": "0000644",
5665
- "sha256": "d8cce5b44106009987d3d32b711a114e4505b4e504bbc6fdfc84f161024ee0cd"
5665
+ "sha256": "fb6503bdaf013a13752968844693cbf957484f033b9df27a66f93eacb91c2cfa"
5666
5666
  },
5667
5667
  {
5668
5668
  "path": "modules/lifecycle/db-versions.js",
@@ -5672,7 +5672,7 @@
5672
5672
  {
5673
5673
  "path": "modules/lifecycle/db.js",
5674
5674
  "mode": "0000644",
5675
- "sha256": "95ff6bc6e9f4ad652dc2aa6bc2c7c520409a2115c714cb4ba867fc80413691e8"
5675
+ "sha256": "a568b425560d8d8f4523995442454233b69016a31934810c5d4b7d83baab0b09"
5676
5676
  },
5677
5677
  {
5678
5678
  "path": "modules/lifecycle/dead-deps.js",
@@ -5902,7 +5902,7 @@
5902
5902
  {
5903
5903
  "path": "modules/lifecycle/routes/task-write-routes.js",
5904
5904
  "mode": "0000644",
5905
- "sha256": "cc86b3c05885c733cc28ba6e41f1b08cb8f0fe652e3181c1c88be4caabf18d61"
5905
+ "sha256": "25bfb492572d553dbd19595addc0c337706ac2c83df71fb04d7fa199b07b715b"
5906
5906
  },
5907
5907
  {
5908
5908
  "path": "modules/lifecycle/routes/tasks.js",
@@ -7727,12 +7727,12 @@
7727
7727
  {
7728
7728
  "path": "package-lock.json",
7729
7729
  "mode": "0000644",
7730
- "sha256": "b271a576a07b28caf13c7d64f1ba8407e3a4d5e53d13accf0da4ad461f94382e"
7730
+ "sha256": "356482b30a64a35a5ae0a670d0b26e0b10dcdd71ae106e8b22fb8f5843282c05"
7731
7731
  },
7732
7732
  {
7733
7733
  "path": "package.json",
7734
7734
  "mode": "0000644",
7735
- "sha256": "e6e4d441aab1b961398eb377d6ac1e4cb9f072efcfa26bd5f51b878b116a8a3e"
7735
+ "sha256": "30e3bed231acb41d36332cae243423aee74a09604e87c966908543fefde74b65"
7736
7736
  },
7737
7737
  {
7738
7738
  "path": "public-docs/index.html",
@@ -8867,7 +8867,7 @@
8867
8867
  {
8868
8868
  "path": "scripts/gds/ship-flow.js",
8869
8869
  "mode": "0000644",
8870
- "sha256": "9853e719e4a695cc52ec9089298d05163210ed3b51ca9eceb7553bc0ed9c65bd"
8870
+ "sha256": "8bf28a3d4d15e83a1e94416ec2519d39bc2a3faa71303a4f2d22d429b6afaa95"
8871
8871
  },
8872
8872
  {
8873
8873
  "path": "scripts/gds/ship-git.js",
@@ -9487,7 +9487,7 @@
9487
9487
  {
9488
9488
  "path": "src/module-api.js",
9489
9489
  "mode": "0000644",
9490
- "sha256": "5394603956c3ec34e8377f82a7f493d7c029f0db1d99b236a0e024fd619fe869"
9490
+ "sha256": "eaa8edfbeec057365723ebfc9d148061b96be50cc37a1ad51b7d050f6e2906c4"
9491
9491
  },
9492
9492
  {
9493
9493
  "path": "src/module-loader/catalog.js",
@@ -9497,7 +9497,7 @@
9497
9497
  {
9498
9498
  "path": "src/module-loader/loader.js",
9499
9499
  "mode": "0000644",
9500
- "sha256": "7c03edea5abee5cce4dfb2f22626e15ee2257ba44f6024e0bf74ff35e8ed0f42"
9500
+ "sha256": "5e417760b49f19d48af3dbd72a90edb53b6a9710ce54ca33052f4fbe86cb9229"
9501
9501
  },
9502
9502
  {
9503
9503
  "path": "src/module-loader/manifest-schema.js",
@@ -9517,7 +9517,7 @@
9517
9517
  {
9518
9518
  "path": "src/module-seams.js",
9519
9519
  "mode": "0000644",
9520
- "sha256": "60cdc1e4ffb22a843a1581909e742b9672fca776bfe5716e72aab77c63df6099"
9520
+ "sha256": "34378efdff13b7e44e38f248026aa927073e0c0c15db670cabaea7aa939c4959"
9521
9521
  },
9522
9522
  {
9523
9523
  "path": "src/modules.js",
@@ -11147,7 +11147,7 @@
11147
11147
  {
11148
11148
  "path": "tests/home_bounded_reads.mjs",
11149
11149
  "mode": "0000644",
11150
- "sha256": "186a8ebd04cd4dc8021b3a36699468e9c0bcf78ca61ce9f7be7c3e25c632fb3e"
11150
+ "sha256": "6853241b64b5f71dfb782956a4fab468a05aa2a9ee41d5159a4cbea5f0424f38"
11151
11151
  },
11152
11152
  {
11153
11153
  "path": "tests/hook_core_resolve.mjs",
@@ -11434,6 +11434,11 @@
11434
11434
  "mode": "0000644",
11435
11435
  "sha256": "7fa36152b6dbb17b2855d261ddbea96af63d26f97001273cd131104235ea431d"
11436
11436
  },
11437
+ {
11438
+ "path": "tests/lifecycle_facade_surface.mjs",
11439
+ "mode": "0000644",
11440
+ "sha256": "cc14604bf130022ce2533247d29187da6260a791f850f73051bd522252d1b115"
11441
+ },
11437
11442
  {
11438
11443
  "path": "tests/linkify_check.mjs",
11439
11444
  "mode": "0000644",
@@ -11552,7 +11557,7 @@
11552
11557
  {
11553
11558
  "path": "tests/migration_allocation.mjs",
11554
11559
  "mode": "0000644",
11555
- "sha256": "0d1bc299555d98aa2ad8571e0a5a185776ca727f95577c3308a7e4b36efbfd97"
11560
+ "sha256": "e9271b1a67d201cfd075ce272b1a5a4f3b66c80dcfa5798ca3ea0d3f5048be43"
11556
11561
  },
11557
11562
  {
11558
11563
  "path": "tests/migration_namespace.mjs",
@@ -11627,7 +11632,7 @@
11627
11632
  {
11628
11633
  "path": "tests/module_loader.mjs",
11629
11634
  "mode": "0000644",
11630
- "sha256": "24aed55c34771eb1bdaec9a51b1e818549298455d50a9ade96db3cc1f06c034b"
11635
+ "sha256": "5d09d5ccedf777c55509dc98365b46c443848c854158ace66fa17c99ba538737"
11631
11636
  },
11632
11637
  {
11633
11638
  "path": "tests/module_manifest.mjs",
@@ -11792,7 +11797,12 @@
11792
11797
  {
11793
11798
  "path": "tests/patch_gate_ordering.mjs",
11794
11799
  "mode": "0000644",
11795
- "sha256": "27a3b5023b6857f3f53734966bc7685757383f2b8dadfa3c0bf1aca2e7db5517"
11800
+ "sha256": "db5e934ef61ddc8dc5804f25a9c84c04f30570db708fd6c77a157e101c69b4d4"
11801
+ },
11802
+ {
11803
+ "path": "tests/patch_value_summary_e2e.mjs",
11804
+ "mode": "0000644",
11805
+ "sha256": "86f60e682284c5f36fa23c6df0ec8b137f40cc548562d6c4496d11d200edb9bf"
11796
11806
  },
11797
11807
  {
11798
11808
  "path": "tests/path_match.mjs",
@@ -11817,7 +11827,7 @@
11817
11827
  {
11818
11828
  "path": "tests/platform_boot.mjs",
11819
11829
  "mode": "0000644",
11820
- "sha256": "fb28eed2a6933805d2af7dc0f8d8e966f921c6556683e1f22b440c2f4a32760d"
11830
+ "sha256": "02a3d4540ddecfe530022df0c6cab4f48258b78efd44224c097622e45b8cc0d5"
11821
11831
  },
11822
11832
  {
11823
11833
  "path": "tests/platform_visibility_gate.mjs",
@@ -12522,7 +12532,7 @@
12522
12532
  {
12523
12533
  "path": "tests/ship_cannot_lie.mjs",
12524
12534
  "mode": "0000644",
12525
- "sha256": "d8d99b6cd465b04eaf13790b0bdfe9a76ce36d77660407d3a2c6a17ac7852e8c"
12535
+ "sha256": "18a3bb613fff3dcc0dfbffb2283a866726af4347c161fc28853b36b5bdd75512"
12526
12536
  },
12527
12537
  {
12528
12538
  "path": "tests/ship_card.mjs",
@@ -183,7 +183,7 @@ export interface PatchProvisioningInstancesIdDetailResponse { ok: boolean; descr
183
183
  export interface PatchProvisioningInstancesIdSettingsResponse { ok: boolean; saved: unknown; planet: unknown; push: unknown }
184
184
  export interface PatchTaskRecommendationsIdRequest { status: "declined" | "withdrawn" }
185
185
  export interface PatchTaskRecommendationsIdResponse { ok: boolean; recommendation: unknown }
186
- export interface PatchTasksIdRequest { kind?: string; discipline?: string; requires_rank?: string; module_key?: string; goal_id?: number; parent_task_id?: StringifiableId; parallel_safe?: unknown; newcomer_friendly?: boolean; xenos_claimable?: boolean; security_sensitive?: boolean; automation_tag?: string; needs_migration?: boolean; description?: string; title?: string; priority?: unknown; status?: unknown }
186
+ export interface PatchTasksIdRequest { kind?: string; discipline?: string; requires_rank?: string; module_key?: string; goal_id?: number; parent_task_id?: StringifiableId; parallel_safe?: unknown; newcomer_friendly?: boolean; xenos_claimable?: boolean; security_sensitive?: boolean; automation_tag?: string; needs_migration?: boolean; description?: string; title?: string; priority?: unknown; value_summary?: string; status?: unknown }
187
187
  export interface PostAccessRequestsInviteRequest { github_login: string }
188
188
  export interface PostAccessRequestsInviteResponse { ok: boolean; already_invited?: boolean; github_login?: unknown; request?: unknown }
189
189
  export interface PostAccessRequestsRequest { github_login: string; display_name?: string; note?: string }
@@ -18981,6 +18981,11 @@
18981
18981
  "maxLength": 200
18982
18982
  },
18983
18983
  "priority": {},
18984
+ "value_summary": {
18985
+ "type": "string",
18986
+ "minLength": 1,
18987
+ "maxLength": 1000
18988
+ },
18984
18989
  "status": {}
18985
18990
  },
18986
18991
  "additionalProperties": false
@@ -593,7 +593,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
593
593
  | GET | `/api/bongos/tasks` | `any-builder` | — | |
594
594
  | POST | `/api/bongos/tasks` | `metic+archon` | `version_id`, `title`, `description`, `status`, `touches`, `est_minutes`, `est_cost_usd`, `manual_degree`, `priority`, `automation_tag`, `credits_reward`, `source`, `source_ref`, `value_summary`, `kind`, `discipline`, `parallel_safe`, `newcomer_friendly`, `security_sensitive`, `requires_rank`, `auto_detect_touches`, `criterion_ids`, `module_key`, `goal_id` | Task create/promote/edit (plan/scope mutation) is Metic+ as of ADR 0090 (owner decision 2026-06-26): a Metic who can run idea-triage may … |
595
595
  | GET | `/api/bongos/tasks/:id` | `any-builder` | — | single-task read; preserved per CRITICAL §4 (supervisor hot path). |
596
- | PATCH | `/api/bongos/tasks/:id` | `metic+archon` | `kind`, `discipline`, `requires_rank`, `module_key`, `goal_id`, `parent_task_id`, `parallel_safe`, `newcomer_friendly`, `xenos_claimable`, `security_sensitive`, `automation_tag`, `needs_migration`, `description`, `title`, `priority`, `status` | PATCH /tasks/:id — supports body.parent_task_id (V2 C2), body.kind (V2 B1), body.discipline (mig 014), body.parallel_safe (mig 028), and … |
596
+ | PATCH | `/api/bongos/tasks/:id` | `metic+archon` | `kind`, `discipline`, `requires_rank`, `module_key`, `goal_id`, `parent_task_id`, `parallel_safe`, `newcomer_friendly`, `xenos_claimable`, `security_sensitive`, `automation_tag`, `needs_migration`, `description`, `title`, `priority`, `value_summary`, `status` | PATCH /tasks/:id — supports body.parent_task_id (V2 C2), body.kind (V2 B1), body.discipline (mig 014), body.parallel_safe (mig 028), and … |
597
597
  | POST | `/api/bongos/tasks/:id/abandon` | `metic+archon` | `reason` | POST /tasks/:id/abandon — terminal "retire this task" transition (task 987). |
598
598
  | POST | `/api/bongos/tasks/:id/attest-gate` | `any-builder` | `head_sha` | POST /tasks/:id/attest-gate — body: { head_sha }. |
599
599
  | POST | `/api/bongos/tasks/:id/confirm` | `any-builder` | `verified` | |
@@ -1743,5 +1743,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
1743
1743
  landed since 1.19.645 with no explicit bump. run 34512629504. (task 1002620)
1744
1744
  1.19.647 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1745
1745
  landed since 1.19.646 with no explicit bump. run 34515733973. (task 1002620)
1746
+ 1.19.648 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1747
+ landed since 1.19.647 with no explicit bump. run 34518387614. (task 1002620)
1748
+ 1.19.649 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1749
+ landed since 1.19.648 with no explicit bump. run 34523111338. (task 1002620)
1746
1750
  ---------------------------------------------------------------------------
1747
1751
  ```
@@ -308,7 +308,7 @@ async function findTaskBySourceRef({ source, sourceRef } = {}, deps = {}) {
308
308
 
309
309
  const TASK_SETTER_COLUMNS = new Set([
310
310
  'kind', 'discipline', 'newcomer_friendly', 'security_sensitive', 'parallel_safe', 'automation_tag',
311
- 'needs_migration', 'module_key', 'description', 'title', 'priority',
311
+ 'needs_migration', 'module_key', 'description', 'title', 'priority', 'value_summary',
312
312
  ]);
313
313
 
314
314
  async function updateTaskColumn(id, column, value) {
@@ -383,6 +383,17 @@ async function updateTaskTitle(id, title) { return updateTaskColumn(id, 'title',
383
383
  // 1 = most urgent, 5 = least; null clears it back to unset (weighted as 5).
384
384
  async function updateTaskPriority(id, priority) { return updateTaskColumn(id, 'priority', priority); }
385
385
 
386
+ // task 1003817: the shipped-value sentence — what the hall, /status and the
387
+ // Discord #ship-news line all read. It was written in exactly ONE place, the
388
+ // claim-resolve route, so a re-grade (which has no claim — regradeMain
389
+ // synthesizes a claimLike precisely because the claim is gone) could correct
390
+ // the summary in the PR and the merge commit and still leave the RECORD holding
391
+ // the first ship's sentence forever. This is the write vector that path uses.
392
+ // Rewritable, never blankable: the route refuses an empty or whitespace-only
393
+ // value, which is this column's form of the COALESCE/NULLIF posture db-ship.js
394
+ // keeps on every other value_summary write.
395
+ async function updateTaskValueSummary(id, valueSummary) { return updateTaskColumn(id, 'value_summary', valueSummary); }
396
+
386
397
 
387
398
  async function updateTaskDiscipline(id, discipline) { return updateTaskColumn(id, 'discipline', discipline); }
388
399
 
@@ -1037,5 +1048,6 @@ module.exports = {
1037
1048
  updateTaskRequiresRank,
1038
1049
  updateTaskSecuritySensitive,
1039
1050
  updateTaskStatus,
1051
+ updateTaskValueSummary,
1040
1052
  versionAcceptsNewWork,
1041
1053
  };
@@ -30,10 +30,16 @@
30
30
  // FACADE (task 1003204, goal 1000079). This file was a 5,748-line monolith; the
31
31
  // R03 de-monolith plan carved it into the twelve db-*.js units required below,
32
32
  // each cohesive and well under the 1,500-line ratchet. The carve was STRUCTURAL:
33
- // no function body changed, and the module.exports block at the bottom is the
34
- // original one verbatim — so every `require('./db')` call site, and every name
33
+ // no function body changed, and every `require('./db')` call site, and every name
35
34
  // reachable through it, behaves exactly as before.
36
35
  //
36
+ // The module.exports block was the carve's ORIGINAL list verbatim until task
37
+ // 1003817, which replaced the db-tasks.js third of it with a spread — see the
38
+ // long note above `const dbTasks` for why, and tests/lifecycle_facade_surface.mjs
39
+ // for the assertion that keeps that surface identical. The other eleven units
40
+ // are still re-listed by name; giving one of them a spread means giving it an
41
+ // omit list and a surface pin too, because a bare spread publishes internals.
42
+ //
37
43
  // The units form a DAG (no require cycles), in dependency order:
38
44
  // db-shared → db-rank-authz → {db-versions, db-goals, db-tasks} → db-analytics
39
45
  // → db-ship → db-claims; db-deps-criteria and db-claim-reads stand alone.
@@ -100,38 +106,37 @@ const {
100
106
  setGoalVisibility,
101
107
  transferGoalOwnership,
102
108
  } = require('./db-goals.js');
103
- const {
104
- REWARD_SUGGESTION_CAP,
105
- createCriterion,
106
- createTask,
107
- findTaskBySourceRef,
108
- generalGoalIdForVersion,
109
- getTask,
110
- listActiveClaims,
111
- listClaimableTasks,
112
- listTasks,
113
- rankCrossDisciplineRecommendations,
114
- rewardGateAssignment,
115
- rewardGateError,
116
- rewardMissing,
117
- setCreditsReward,
118
- suggestCreditsReward,
119
- taskTouchesProtectedPath,
120
- updateTaskAutomationTag,
121
- updateTaskDescription,
122
- updateTaskTitle,
123
- updateTaskDiscipline,
124
- updateTaskGoal,
125
- updateTaskKind,
126
- updateTaskModuleKey,
127
- updateTaskNeedsMigration,
128
- updateTaskNewcomerFriendly,
129
- updateTaskParallelSafe,
130
- updateTaskPriority,
131
- updateTaskRequiresRank,
132
- updateTaskSecuritySensitive,
133
- updateTaskStatus,
134
- } = require('./db-tasks.js');
109
+ // task 1003817: db-tasks.js's exports ARE this facade's task surface, so they are
110
+ // spread into module.exports below instead of being re-listed name by name.
111
+ //
112
+ // WHY THE TWIN LIST HAD TO GO. What stood here was 31 sorted names destructured
113
+ // from db-tasks.js purely so module.exports could name them again — not one was
114
+ // called anywhere inside this file. Two hand-maintained copies of one list is
115
+ // real duplication, and the `duplicate_window_count` ratchet reads it as such:
116
+ // the lists were 29 identical normalized lines, one under the 30-line window, so
117
+ // task 1003750 adding `updateTaskPriority` to both pushed the run to 30 and cost
118
+ // an owner-decided baseline raise (59 → 60). That decision is recorded in
119
+ // config/fitness-baselines.json along with the instruction for whoever added the
120
+ // NEXT db-tasks function — this task, adding updateTaskValueSummary: fix the
121
+ // facade, do not raise the baseline again. Spreading removes the whole class, so
122
+ // the next setter costs nothing.
123
+ //
124
+ // TASK_FACADE_OMIT is what keeps this surface-PRESERVING rather than quietly
125
+ // wider. Two of db-tasks.js's 33 exports were deliberately never re-exported
126
+ // here, and a bare spread would publish them — which ADR 0091 §3 treats as a
127
+ // real surface change, not a tidy-up. Naming them means dropping one has to be
128
+ // somebody's decision. tests/lifecycle_facade_surface.mjs pins the resulting key
129
+ // set, so this can never silently widen or narrow.
130
+ const dbTasks = require('./db-tasks.js');
131
+ const TASK_FACADE_OMIT = new Set([
132
+ // Internal to db-tasks.js's own queries — a SQL fragment, not a callable.
133
+ 'CREDITS_AWARDED_SUBQUERY',
134
+ // The version-gate predicate; routes reach it through db-versions, not here.
135
+ 'versionAcceptsNewWork',
136
+ ]);
137
+ const dbTasksFacade = Object.fromEntries(
138
+ Object.entries(dbTasks).filter(([name]) => !TASK_FACADE_OMIT.has(name))
139
+ );
135
140
  const {
136
141
  addDependency,
137
142
  addTaskCriterion,
@@ -214,6 +219,7 @@ const {
214
219
  } = require('./db-claims.js');
215
220
 
216
221
  module.exports = {
222
+ ...dbTasksFacade,
217
223
  tallyPeerVotes,
218
224
  PEER_VOTE_KARMA_LAG_DAYS,
219
225
  RANKS,
@@ -263,19 +269,6 @@ module.exports = {
263
269
  listCriteriaForGoal,
264
270
  archiveGoalWithDispositions,
265
271
  openTasksInGoal,
266
- createTask,
267
- generalGoalIdForVersion,
268
- findTaskBySourceRef,
269
- getTask,
270
- listTasks,
271
- updateTaskGoal,
272
- createCriterion,
273
- updateTaskKind,
274
- updateTaskDescription,
275
- updateTaskTitle,
276
- updateTaskDiscipline,
277
- updateTaskNewcomerFriendly,
278
- updateTaskRequiresRank,
279
272
  xenosClaimAllowed,
280
273
  rankAllowsTask,
281
274
  REQUIRES_RANK_TIER,
@@ -290,31 +283,14 @@ module.exports = {
290
283
  LIVE_RANK_LADDER,
291
284
  deriveRequiredRank,
292
285
  highestRank,
293
- rewardMissing,
294
- suggestCreditsReward,
295
- REWARD_SUGGESTION_CAP,
296
- rewardGateAssignment,
297
- rewardGateError,
298
- setCreditsReward,
299
286
  THETES_GRADUATION_THRESHOLD,
300
287
  qualifiesForThetesGraduation,
301
288
  countShippedTasksForBuilder,
302
289
  singleRungPromotionError,
303
290
  disambiguateWorktreeName,
304
291
  foldWorktreeName,
305
- updateTaskParallelSafe,
306
- updateTaskPriority,
307
- updateTaskSecuritySensitive,
308
- updateTaskAutomationTag,
309
- updateTaskNeedsMigration,
310
- updateTaskModuleKey,
311
- listClaimableTasks,
312
292
  capNewcomerTasks,
313
- taskTouchesProtectedPath, // SR-12 / G2 (task 994): autonomous-fit protected-path exclusion (pure, testable)
314
293
  decorateGoalMembership, // task 1003137: feed-side joinable flag (pure, testable)
315
- rankCrossDisciplineRecommendations,
316
- listActiveClaims,
317
- updateTaskStatus,
318
294
  getTaskDependencies,
319
295
  getTaskDependents,
320
296
  attachTaskDepSummaries,
@@ -326,6 +326,14 @@ module.exports = function registerTaskWriteRoutes(router) {
326
326
  // A whole-goal re-rank (needed-vs-nice) had no route and had to run as SQL.
327
327
  // null clears it back to unset; the 1..5 bounds mirror tasks_priority_check.
328
328
  priority: {}, // 1..5 or null — checked inline
329
+ // task 1003817: the shipped-value sentence. Written in exactly one place
330
+ // before this — the claim-resolve route — so `ship.js --regrade --summary`
331
+ // (no claim, by definition) could correct it in the PR and the merge commit
332
+ // and still leave the hall, /status and the Discord ship line reading the
333
+ // FIRST ship's text forever. Same task.edit permission and audit_log trail
334
+ // as the title/description/priority edits above; minLength 1 (plus the
335
+ // whitespace check below) so the record can be rewritten, never blanked.
336
+ value_summary: { type: 'string', minLength: 1, maxLength: LIMITS.VALUE_SUMMARY },
329
337
  // task 1002614 (map D-18): DECLARED so strict-mode admits the request far
330
338
  // enough to reach the deliberate status_not_patchable hint below (#514) —
331
339
  // the hint had been unreachable dead code behind unknown_field since the
@@ -354,6 +362,7 @@ module.exports = function registerTaskWriteRoutes(router) {
354
362
  const hasDescription = Object.prototype.hasOwnProperty.call(body, 'description');
355
363
  const hasTitle = Object.prototype.hasOwnProperty.call(body, 'title');
356
364
  const hasPriority = Object.prototype.hasOwnProperty.call(body, 'priority');
365
+ const hasValueSummary = Object.prototype.hasOwnProperty.call(body, 'value_summary');
357
366
  // #514: status is a lifecycle field, not a PATCH-able column — it moves only
358
367
  // through the dedicated transition routes. The #506 session tried
359
368
  // `PATCH status=ready`, got the generic no_supported_fields error, and had
@@ -365,8 +374,8 @@ module.exports = function registerTaskWriteRoutes(router) {
365
374
  if (Object.prototype.hasOwnProperty.call(body, 'status')) {
366
375
  return res.fail('status_not_patchable', { status: 400, message: STATUS_HINT });
367
376
  }
368
- if (!hasParent && !hasKind && !hasDiscipline && !hasNewcomerOrXenos && !hasParallelSafe && !hasSecuritySensitive && !hasAutomationTag && !hasNeedsMigration && !hasRequiresRank && !hasModuleKey && !hasGoalId && !hasDescription && !hasTitle && !hasPriority) {
369
- return res.fail('no_supported_fields', { status: 400, message: 'PATCH /tasks/:id supports parent_task_id, kind, discipline, title, description, priority, parallel_safe, security_sensitive, automation_tag, needs_migration, requires_rank, module_key, goal_id, and newcomer_friendly (alias: xenos_claimable). ' + STATUS_HINT });
377
+ if (!hasParent && !hasKind && !hasDiscipline && !hasNewcomerOrXenos && !hasParallelSafe && !hasSecuritySensitive && !hasAutomationTag && !hasNeedsMigration && !hasRequiresRank && !hasModuleKey && !hasGoalId && !hasDescription && !hasTitle && !hasPriority && !hasValueSummary) {
378
+ return res.fail('no_supported_fields', { status: 400, message: 'PATCH /tasks/:id supports parent_task_id, kind, discipline, title, description, value_summary, priority, parallel_safe, security_sensitive, automation_tag, needs_migration, requires_rank, module_key, goal_id, and newcomer_friendly (alias: xenos_claimable). ' + STATUS_HINT });
370
379
  }
371
380
  // ---- task 1002609 (G-22): settle EVERY gate and validation before the ----
372
381
  // ---- FIRST write. The previous shape validated-and-wrote per field in ----
@@ -393,6 +402,14 @@ module.exports = function registerTaskWriteRoutes(router) {
393
402
  return res.fail('bad_priority', { status: 400, message: `priority must be an integer ${TASK_FIELD_BOUNDS.priority.min}-${TASK_FIELD_BOUNDS.priority.max} (1 = most urgent), or null to clear` });
394
403
  }
395
404
  }
405
+ // task 1003817: minLength 1 refuses "" but not " ", and the write trims —
406
+ // so without this a whitespace-only body would blank the shipped-value
407
+ // record through the one field whose whole point is that it survives. An
408
+ // omitted field writes nothing at all (hasValueSummary is false), which is
409
+ // this route's form of the COALESCE/NULLIF posture db-ship.js keeps.
410
+ if (hasValueSummary && body.value_summary.trim() === '') {
411
+ return res.fail('bad_value_summary', { status: 400, message: 'value_summary must be a non-empty sentence — the shipped-value record can be rewritten, never blanked. Omit the field to leave it unchanged.' });
412
+ }
396
413
  if (hasSecuritySensitive) {
397
414
  // task 990: explicit Archon-only-broadcast flag. Boolean only (no null) —
398
415
  // it gates whether ship.js routes the broadcast to the Archon-only
@@ -556,6 +573,15 @@ module.exports = function registerTaskWriteRoutes(router) {
556
573
  return res.fail('patch_failed', { status: 500, message: 'internal error' });
557
574
  }
558
575
  }
576
+ if (hasValueSummary) {
577
+ try {
578
+ updated = await db.updateTaskValueSummary(id, body.value_summary.trim());
579
+ if (!updated) return res.fail('task_not_found', 404);
580
+ } catch (err) {
581
+ log.error('[gds] PATCH /tasks/:id value_summary', err);
582
+ return res.fail('patch_failed', { status: 500, message: 'internal error' });
583
+ }
584
+ }
559
585
  if (hasGoalId) {
560
586
  try {
561
587
  updated = await db.updateTaskGoal(id, body.goal_id);
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.647",
3
+ "version": "1.19.649",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.647",
9
+ "version": "1.19.649",
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.647",
3
+ "version": "1.19.649",
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",
@@ -659,6 +659,58 @@ async function shipMain() {
659
659
  });
660
660
  }
661
661
 
662
+ // persistRegradeSummary — store a re-grade's corrected --summary on the task row
663
+ // (task 1003817). A re-grade has no claim, so it cannot reach claim-resolve, the
664
+ // only writer of tasks.value_summary before this; PATCH /tasks/:id is the route
665
+ // it uses instead.
666
+ //
667
+ // WHY PATCH AND NOT publish-branch, which already RECEIVES value_summary and
668
+ // could have persisted it with a one-line change: publish-branch is only called
669
+ // when pushVia() === 'server'. A laptop land goes through ciLandLocal (gh), and
670
+ // --no-merge / --db-only / --allow-empty never land at all — so persisting there
671
+ // would have fixed one path of four and left the defect standing on the rest. It
672
+ // would also have made publish-branch a SECOND writer racing claim-resolve on
673
+ // the normal ship path, which sends the same field. Writing from here keeps one
674
+ // writer per ship: the claim on a first ship, this on a re-grade.
675
+ //
676
+ // Three deliberate postures:
677
+ // - OMITTED --summary writes NOTHING. The stored value is the fallback the
678
+ // grader was already handed, so a re-grade that omits the flag must leave
679
+ // the row exactly as it was — the COALESCE/NULLIF posture every other
680
+ // value_summary write in db-ship.js keeps. This is acceptance 2.
681
+ // - UNCHANGED text writes nothing either, so a no-op edit mints no audit_log row.
682
+ // - A FAILURE is LOUD BUT NON-BLOCKING. The grade is the expensive step and it
683
+ // has not run yet; discarding a re-grade over a summary that did not persist
684
+ // would trade the cheap problem for the costly one. The builder is told
685
+ // plainly that the row still holds the old sentence, so nothing is silent.
686
+ // An older pinned core that has not learned the field is named as exactly
687
+ // that rather than reported as a generic error — the same forward-compat
688
+ // posture the resolve call in shipMain documents at length (task 1003767),
689
+ // because @cloudbongos/cli ships separately from the core and an instance
690
+ // pins its own core version.
691
+ async function persistRegradeSummary({ taskId, summaryArg, task }) {
692
+ const next = (summaryArg || '').trim();
693
+ if (!next) return { written: false, reason: 'omitted' };
694
+ if (next === (task.value_summary || '').trim()) return { written: false, reason: 'unchanged' };
695
+ let r;
696
+ try {
697
+ r = await (await cliClient()).tasks.patchTasksId({ id: taskId, body: { value_summary: next } });
698
+ } catch (err) {
699
+ console.error(` ⚠ value_summary NOT stored (${err && err.message ? err.message : 'request failed'}) — task #${taskId} keeps its previous summary. The grade below is unaffected.`);
700
+ return { written: false, reason: 'error' };
701
+ }
702
+ if (!r.ok) {
703
+ if (isUnknownFieldRejection(r.status, r.data, 'value_summary')) {
704
+ console.error(` ⚠ value_summary NOT stored: this instance's core predates PATCH /tasks/:id accepting the field. Upgrade the core, or edit the summary in the hall. The grade below is unaffected.`);
705
+ } else {
706
+ console.error(` ⚠ value_summary NOT stored (${apiErrorLine(r.data, r.status)}) — task #${taskId} keeps its previous summary. The grade below is unaffected.`);
707
+ }
708
+ return { written: false, reason: 'refused' };
709
+ }
710
+ console.log(` value_summary updated on task #${taskId} — the hall, /status and the ship broadcast now read the corrected sentence.`);
711
+ return { written: true };
712
+ }
713
+
662
714
  // ---------- Re-grade flow (#678) ----------
663
715
  //
664
716
  // `node scripts/gds/ship.js <id> --regrade` closes the task-595-regrade-gap
@@ -676,6 +728,7 @@ async function shipMain() {
676
728
  // - grade: gradeAndRecord → POST /tasks/:id/grade (rank-open; applyGrade
677
729
  // promotes completed→confirmed + awards credits on pass, idempotent)
678
730
  // - on pass: the normal confirmed→shipped merge + sync tail runs.
731
+
679
732
  async function regradeMain() {
680
733
  await requireSession();
681
734
  const args = process.argv.slice(2);
@@ -764,16 +817,14 @@ async function regradeMain() {
764
817
  // task's stored value_summary so the merge commit + Discord #ship-news post
765
818
  // carry meaningful text even when --summary is omitted.
766
819
  //
767
- // AND IT NEVER REACHES THE TASK ROW — task 1003702's acceptance 3 asked this
768
- // question; the answer is a real, separate defect, filed as task 1003817.
769
- // `summary` from here feeds the grader prompt, the merge commit message and
770
- // the PR title/body, and NOTHING writes it back to tasks.value_summary. All
771
- // three candidate write paths were read and ruled out: publish-branch accepts
772
- // value_summary but uses it only for PR text; PATCH /tasks/:id does not accept
773
- // the field at all; the claim-resolve route is the sole writer and a re-grade
774
- // has no claim (which is why claimLike is synthesized above). Fixing it needs
775
- // a server route change, so it is deliberately NOT folded into this CLI fix.
776
- // tests/ship_cannot_lie.mjs pins both halves of that so the answer cannot rot.
820
+ // AND IT NOW REACHES THE TASK ROW TOO — task 1003817. It did not before: this
821
+ // `summary` fed the grader prompt, the merge commit message and the PR
822
+ // title/body and stopped there, so a builder who re-graded with a CORRECTED
823
+ // --summary saw it applied everywhere except the place that is the record —
824
+ // the hall, /status and the Discord #ship-news line all kept the first ship's
825
+ // sentence forever. persistRegradeSummary (below the preflight) is the write;
826
+ // PATCH /tasks/:id is the route it uses, and `value_summary` was added to that
827
+ // schema by this same task. tests/ship_cannot_lie.mjs pins the wiring.
777
828
  const summary = summaryArg || task.value_summary || '';
778
829
 
779
830
  // The grader helpers want a claim-like object for touches[] (preflight's
@@ -804,6 +855,14 @@ async function regradeMain() {
804
855
  process.exit(1);
805
856
  }
806
857
 
858
+ // ---- Persist a corrected --summary to the task row (task 1003817) ----
859
+ // Placed HERE deliberately: after the guards that can still abort the re-grade
860
+ // (so a refused run writes nothing), and BEFORE the grade — which is the order
861
+ // the normal ship already has, where claim-resolve stores value_summary and
862
+ // the panel runs afterwards. The row, the grader prompt, the merge commit and
863
+ // the PR then all carry the same sentence.
864
+ await persistRegradeSummary({ taskId, summaryArg, task });
865
+
807
866
  // ---- Delta-aware prior-round context (task 1002662) ----
808
867
  // The prior grade's findings + the builder's response + the interim fix diff
809
868
  // ride to the panel so an already-answered finding can't re-veto on
package/src/module-api.js CHANGED
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
71
71
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
72
72
  // the entry to that file. Look for a version's history there, not here.
73
73
  // ---------------------------------------------------------------------------
74
- const CORE_VERSION = '1.19.647'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.649'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
75
75
 
76
76
  // A namespaced logger so a module's log lines are attributable + consistent.
77
77
  // Usage: const log = api.logger('dev-box'); log.info('mounted');