@bongos/core 1.19.588 → 1.19.590
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 +31 -21
- package/clients/bongos-client/index.d.ts +3 -2
- package/docs/adr/0264-the-ten-working-areas.md +144 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +30 -4
- package/docs/api-reference.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/modules/lifecycle/db-goals.js +126 -1
- package/modules/lifecycle/db.js +2 -0
- package/modules/lifecycle/routes/goal-route-authz.js +134 -0
- package/modules/lifecycle/routes/goals.js +106 -8
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/goal_archive_disposition.mjs +207 -6
- package/tests/goal_archive_route_e2e.mjs +135 -0
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.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.590",
|
|
6
|
+
"core_contract": "1.19.590",
|
|
7
|
+
"source_commit": "250f70f068d2406d49d2b57306a8f3f1ebf0f1ab",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-08T02:
|
|
9
|
+
"built_at": "2026-09-08T02:32:23.112Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 457,
|
|
13
13
|
"agent_docs_stubbed": 24,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2061,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2542,
|
|
20
|
+
"tree_sha256": "ff91f93bb16afd98f8d2130615c602425ac4d133c4b01c83fbe94e6d8015f2cc",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -392,7 +392,7 @@
|
|
|
392
392
|
{
|
|
393
393
|
"path": "clients/bongos-client/index.d.ts",
|
|
394
394
|
"mode": "0000644",
|
|
395
|
-
"sha256": "
|
|
395
|
+
"sha256": "3865a914bd9e50df23960dbad5783bd1584157ff2b2bfcd2b8e597a620e81c01"
|
|
396
396
|
},
|
|
397
397
|
{
|
|
398
398
|
"path": "clients/bongos-client/index.mjs",
|
|
@@ -1834,20 +1834,25 @@
|
|
|
1834
1834
|
"mode": "0000644",
|
|
1835
1835
|
"sha256": "07a3561c7bc7f55bb2077925df4df6a24b68a1f0c8d4052ac50278edd9d4eba1"
|
|
1836
1836
|
},
|
|
1837
|
+
{
|
|
1838
|
+
"path": "docs/adr/0264-the-ten-working-areas.md",
|
|
1839
|
+
"mode": "0000644",
|
|
1840
|
+
"sha256": "03f1cf19a181d6726066ba6fb72156add0cb8ede004ebc7ddf8ac3859a335596"
|
|
1841
|
+
},
|
|
1837
1842
|
{
|
|
1838
1843
|
"path": "docs/adr/README.md",
|
|
1839
1844
|
"mode": "0000644",
|
|
1840
|
-
"sha256": "
|
|
1845
|
+
"sha256": "b38a32c8557012f9e5e761c5e219901ad628af9b63d2cb190d32e783dbf9aaa9"
|
|
1841
1846
|
},
|
|
1842
1847
|
{
|
|
1843
1848
|
"path": "docs/api-reference.md",
|
|
1844
1849
|
"mode": "0000644",
|
|
1845
|
-
"sha256": "
|
|
1850
|
+
"sha256": "5e2031bf695567cb4318ef792395150b7032604e0e4cd7cafee99f05e1d3cb01"
|
|
1846
1851
|
},
|
|
1847
1852
|
{
|
|
1848
1853
|
"path": "docs/api/openapi.json",
|
|
1849
1854
|
"mode": "0000644",
|
|
1850
|
-
"sha256": "
|
|
1855
|
+
"sha256": "597f489b67a88154ee771baa8b378bf681594534cf7690c2ffd9c109cb26092d"
|
|
1851
1856
|
},
|
|
1852
1857
|
{
|
|
1853
1858
|
"path": "docs/architecture.md",
|
|
@@ -2722,7 +2727,7 @@
|
|
|
2722
2727
|
{
|
|
2723
2728
|
"path": "docs/module-api-changelog.md",
|
|
2724
2729
|
"mode": "0000644",
|
|
2725
|
-
"sha256": "
|
|
2730
|
+
"sha256": "c2736a86c941e12d08b38f798ab18eddf8a365ef709ed952778629d13ebf4e1d"
|
|
2726
2731
|
},
|
|
2727
2732
|
{
|
|
2728
2733
|
"path": "docs/modules-contract.md",
|
|
@@ -5507,7 +5512,7 @@
|
|
|
5507
5512
|
{
|
|
5508
5513
|
"path": "modules/lifecycle/db-goals.js",
|
|
5509
5514
|
"mode": "0000644",
|
|
5510
|
-
"sha256": "
|
|
5515
|
+
"sha256": "392970a48349421c000de9581cfcc63969152179feb3406f1057571d9b68c0ee"
|
|
5511
5516
|
},
|
|
5512
5517
|
{
|
|
5513
5518
|
"path": "modules/lifecycle/db-grade.js",
|
|
@@ -5547,7 +5552,7 @@
|
|
|
5547
5552
|
{
|
|
5548
5553
|
"path": "modules/lifecycle/db.js",
|
|
5549
5554
|
"mode": "0000644",
|
|
5550
|
-
"sha256": "
|
|
5555
|
+
"sha256": "c3318a8473ccc9b41bff5eb94fe4096032c2d5c5a7819d0777c4d41db0e4d695"
|
|
5551
5556
|
},
|
|
5552
5557
|
{
|
|
5553
5558
|
"path": "modules/lifecycle/dead-deps.js",
|
|
@@ -5727,12 +5732,12 @@
|
|
|
5727
5732
|
{
|
|
5728
5733
|
"path": "modules/lifecycle/routes/goal-route-authz.js",
|
|
5729
5734
|
"mode": "0000644",
|
|
5730
|
-
"sha256": "
|
|
5735
|
+
"sha256": "3b4fb0168b29fb0df1260c23c5b14d96576f00f587dd5c188fe17a712e3dffaa"
|
|
5731
5736
|
},
|
|
5732
5737
|
{
|
|
5733
5738
|
"path": "modules/lifecycle/routes/goals.js",
|
|
5734
5739
|
"mode": "0000644",
|
|
5735
|
-
"sha256": "
|
|
5740
|
+
"sha256": "28874a1b4d3e748866fa59d510c3c62656ad48039dae8ca7ce5b0ca5d47b357d"
|
|
5736
5741
|
},
|
|
5737
5742
|
{
|
|
5738
5743
|
"path": "modules/lifecycle/routes/help-requests.js",
|
|
@@ -7597,12 +7602,12 @@
|
|
|
7597
7602
|
{
|
|
7598
7603
|
"path": "package-lock.json",
|
|
7599
7604
|
"mode": "0000644",
|
|
7600
|
-
"sha256": "
|
|
7605
|
+
"sha256": "035ec486f26d0a18ffcc578a861b2758f79ea279371c4f88dfe5fdfa1b04fefe"
|
|
7601
7606
|
},
|
|
7602
7607
|
{
|
|
7603
7608
|
"path": "package.json",
|
|
7604
7609
|
"mode": "0000644",
|
|
7605
|
-
"sha256": "
|
|
7610
|
+
"sha256": "05c57c598f80ce768e83bc0040feb275732545150cbcb3cd01341ff3633ea96f"
|
|
7606
7611
|
},
|
|
7607
7612
|
{
|
|
7608
7613
|
"path": "public-docs/index.html",
|
|
@@ -9312,7 +9317,7 @@
|
|
|
9312
9317
|
{
|
|
9313
9318
|
"path": "src/module-api.js",
|
|
9314
9319
|
"mode": "0000644",
|
|
9315
|
-
"sha256": "
|
|
9320
|
+
"sha256": "b2b2d8c9ccf065e7bbc7b984ff9acda5f4400c9db4e9063678e6d996616f7d1c"
|
|
9316
9321
|
},
|
|
9317
9322
|
{
|
|
9318
9323
|
"path": "src/module-loader/catalog.js",
|
|
@@ -10432,7 +10437,12 @@
|
|
|
10432
10437
|
{
|
|
10433
10438
|
"path": "tests/goal_archive_disposition.mjs",
|
|
10434
10439
|
"mode": "0000644",
|
|
10435
|
-
"sha256": "
|
|
10440
|
+
"sha256": "bf0baad459ee2e103079a53570ca4998bb3ba66451ad0257478dd236118c8ce6"
|
|
10441
|
+
},
|
|
10442
|
+
{
|
|
10443
|
+
"path": "tests/goal_archive_route_e2e.mjs",
|
|
10444
|
+
"mode": "0000644",
|
|
10445
|
+
"sha256": "ffefb97db455e2b6656f5c26933312572102e3a8aa37605545ae9b425d616be0"
|
|
10436
10446
|
},
|
|
10437
10447
|
{
|
|
10438
10448
|
"path": "tests/goal_category.mjs",
|
|
@@ -252,7 +252,8 @@ export interface PostGithubRepoVisibilityRequest { repo: string; visibility: "pu
|
|
|
252
252
|
export interface PostGithubRepoVisibilityResponse { ok: boolean; repo: unknown; private_deployable: boolean }
|
|
253
253
|
export interface PostGithubReposRequest { name: string }
|
|
254
254
|
export interface PostGithubReposResponse { repo: unknown }
|
|
255
|
-
export interface
|
|
255
|
+
export interface PostGoalsIdArchiveRequest { reason?: string; dispositions?: Record<string, unknown> }
|
|
256
|
+
export interface PostGoalsIdArchiveResponse { ok: boolean; goal: unknown; applied: unknown }
|
|
256
257
|
export interface PostGoalsIdConflictsResolveRequest { action: string; note?: string; touches?: string[]; first_task_id?: StringifiableId; then_task_id?: StringifiableId; task_id?: StringifiableId }
|
|
257
258
|
export interface PostGoalsIdConflictsResolveResponse { ok: boolean; action: unknown; first_task_id?: unknown; then_task_id?: unknown; dependencies?: unknown; claim?: unknown; task_id?: unknown; touches?: unknown; requires_rank?: unknown }
|
|
258
259
|
export interface PostGoalsIdInvitationsReqIdRescindResponse { ok: boolean; request: unknown }
|
|
@@ -676,7 +677,7 @@ export interface BongosClient {
|
|
|
676
677
|
/** PATCH /goals/{id}/accepting-requests — rank: any-builder */
|
|
677
678
|
patchGoalsIdAcceptingRequests(args?: RequestArgs): Promise<ApiResponse>;
|
|
678
679
|
/** POST /goals/{id}/archive — rank: metic+archon */
|
|
679
|
-
postGoalsIdArchive(args?: RequestArgs): Promise<PostGoalsIdArchiveResponse>;
|
|
680
|
+
postGoalsIdArchive(args?: RequestArgs & { body?: PostGoalsIdArchiveRequest }): Promise<PostGoalsIdArchiveResponse>;
|
|
680
681
|
/** PATCH /goals/{id}/category — rank: any-builder */
|
|
681
682
|
patchGoalsIdCategory(args?: RequestArgs): Promise<ApiResponse>;
|
|
682
683
|
/** GET /goals/{id}/conflicts — rank: any-builder */
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# 0264 — The ten working areas: one goal per area, held until 5,000 builders
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-09-07
|
|
5
|
+
- **Tasks:** [#1003696](https://cloudbongos.com/builders#/task/1003696) (this record). Executed by BV1.R25 ([#1003612](https://cloudbongos.com/builders#/task/1003612)), R26 ([#1003613](https://cloudbongos.com/builders#/task/1003613)), R27 ([#1003614](https://cloudbongos.com/builders#/task/1003614)), R28 ([#1003615](https://cloudbongos.com/builders#/task/1003615)) and R29 ([#1003617](https://cloudbongos.com/builders#/task/1003617)).
|
|
6
|
+
- **Goal:** [#1000086](https://cloudbongos.com/builders#/goal/1000086) — Strict versioning, criterion `sv-cutover-proven-live`.
|
|
7
|
+
- **Decided by:** the owner and the full team, in a manual goal review on 2026-09-07.
|
|
8
|
+
- **Builds on:** [ADR 0250](<redacted>.md) (the version boundary is the scope gate — this is the scope that boundary will hold), [ADR 0086](<redacted>.md) (the goal tier).
|
|
9
|
+
|
|
10
|
+
## 1. The decision
|
|
11
|
+
|
|
12
|
+
**The platform runs on exactly ten goals — one per working area — until it has
|
|
13
|
+
5,000 builders.** Each area has a named owner and a description of what "done"
|
|
14
|
+
looks like at that scale. Thirty-eight goals are open today; they collapse into
|
|
15
|
+
these ten.
|
|
16
|
+
|
|
17
|
+
| # | Working area | Owner | The 5,000-builder state |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| 1 | **Project creation** | Rini | Any new user, developer or not, can create a project from an existing folder or code repository whatever it already contains. |
|
|
20
|
+
| 2 | **Account creation, management & community** | Rini | — |
|
|
21
|
+
| 3 | **Human project management** | Scott | A project-management system with scalable levels of hierarchy — not infinitely configurable. Users edit, scope and manage work without ever prompting Claude. |
|
|
22
|
+
| 4 | **Bongos Core distribution** (staging of updates) | Lars / Will | Core updates are offered in every project's UI, and via the update CLI or skill. Approved users decide whether to take an update. A changelog is visible inside the update. |
|
|
23
|
+
| 5 | **Module distribution & economy** | Will | Modules are bought with money or credits and update the same way. Bongos assesses and ranks module quality; willing users test before mass deployment; only the best modules are listed, on price and assessed quality. |
|
|
24
|
+
| 6 | **Governor / Builder / Artist / Ideator experience** (incl. agents) | Masterqua / Rini | Each role gets a tailored experience that cannot break into the others. Noise is limited at every human interface. Agents are designed to aid all these roles. |
|
|
25
|
+
| 7 | **Government** | Will / Masterqua | Fully customizable creation of ranks and what each entails for project access. Recommended base ranks and governments ship for different project types and sizes. |
|
|
26
|
+
| 8 | **Platform credit economy** | Scott | Real money reaches users for completed work, without fail. Users hold platform balances, fund projects and pay each other with no fees. Bongos banks project funds and delivers them on cash-out. |
|
|
27
|
+
| 9 | **Platform analytics** | Lars | Bongos gathers as much user data as it can to improve itself — session transcripts as an efficiency signal, plus every user action and work delivery across the ecosystem through the Bongos API. |
|
|
28
|
+
| 10 | **Security** | all except Nils and Scott | Ranks are enforced. The Bongos server is not hackable by Claude's latest model. Red-teaming is consistent. |
|
|
29
|
+
|
|
30
|
+
## 2. Why ten, and why now
|
|
31
|
+
|
|
32
|
+
Thirty-eight open goals is not a plan; it is a list of everything anyone has ever
|
|
33
|
+
wanted. [ADR 0250](<redacted>.md) measured what that costs — 523 open tasks, fourteen goals
|
|
34
|
+
reporting done while holding thirty-five unfinished tasks — and built the
|
|
35
|
+
machinery to stop the drift. But **enforcement alone cannot shrink a scope that
|
|
36
|
+
is already too large**: D1 closes the goal set of a *building* version, which
|
|
37
|
+
freezes thirty-eight goals in place just as effectively as it would freeze ten.
|
|
38
|
+
|
|
39
|
+
So the rules and the cut are two halves of one move. This ADR is the cut. Ten is
|
|
40
|
+
chosen to be small enough that every area has a single owner who can hold its
|
|
41
|
+
whole shape in their head, and the horizon is **5,000 builders** rather than a
|
|
42
|
+
date, because the point at which this set stops being the right set is a scale
|
|
43
|
+
question, not a calendar one.
|
|
44
|
+
|
|
45
|
+
## 3. The disposition of all 38 open goals
|
|
46
|
+
|
|
47
|
+
Nothing on this list is a guess. Every row below the horizontal rule was named in
|
|
48
|
+
the review; the three rows above it were not, and are marked as such.
|
|
49
|
+
|
|
50
|
+
### Not covered by the review — open questions
|
|
51
|
+
|
|
52
|
+
| Goal | Note |
|
|
53
|
+
|---|---|
|
|
54
|
+
| [#1000003](https://cloudbongos.com/builders#/goal/1000003) — Work with no goal yet | The per-version catch-all. Already R25's target: drained and archived there. No separate decision needed. |
|
|
55
|
+
| [#1000065](https://cloudbongos.com/builders#/goal/1000065) — Community surface | Reads as the community half of area 2 and is treated as such below, but the review did not say so. **Confirm before executing R25.** |
|
|
56
|
+
| [#1000088](https://cloudbongos.com/builders#/goal/1000088) — Repo cleanup: everything justifies itself | Genuinely unmentioned, and maps to no area. **Needs an owner decision: fold, keep as an eleventh, or close.** |
|
|
57
|
+
|
|
58
|
+
### Becomes an area, or folds into one
|
|
59
|
+
|
|
60
|
+
| Goal | Disposition | Area |
|
|
61
|
+
|---|---|---|
|
|
62
|
+
| [#1000046](https://cloudbongos.com/builders#/goal/1000046) — Project creation | **becomes the area** (active; Rini building, Lars on the npm package) | 1 |
|
|
63
|
+
| [#1000065](https://cloudbongos.com/builders#/goal/1000065) — Community surface | becomes the area *(assumed — see above)* | 2 |
|
|
64
|
+
| [#1000045](https://cloudbongos.com/builders#/goal/1000045) — Public builder profiles | fold — relevant, active | 2 |
|
|
65
|
+
| [#1000075](https://cloudbongos.com/builders#/goal/1000075) — Collaboration & the social hall | fold — Niels almost done; builder collaboration is refined under community | 2 |
|
|
66
|
+
| [#1000085](https://cloudbongos.com/builders#/goal/1000085) — The operable hall | fold | 3 |
|
|
67
|
+
| [#1000054](https://cloudbongos.com/builders#/goal/1000054) — Public distribution | fold — Rini and Lars, relevant | 4 |
|
|
68
|
+
| [#1000029](https://cloudbongos.com/builders#/goal/1000029) — Module upstreaming & catalog | fold — looped into the module goal | 5 |
|
|
69
|
+
| [#1000066](https://cloudbongos.com/builders#/goal/1000066) — Role-composed context | fold — this is the working-area goal | 6 |
|
|
70
|
+
| [#1000038](https://cloudbongos.com/builders#/goal/1000038) — Agent framework & registry | fold — stays, needs cleanup from Rini; dedicated agents for specific tasks | 6 |
|
|
71
|
+
| [#1000039](https://cloudbongos.com/builders#/goal/1000039) — Agent authoring & first roster | fold into the agents one | 6 |
|
|
72
|
+
| [#1000040](https://cloudbongos.com/builders#/goal/1000040) — Agent scheduling & spend limits | fold into the agents one | 6 |
|
|
73
|
+
| [#1000025](https://cloudbongos.com/builders#/goal/1000025) — Builder experience | review first, close out solid tasks, then fold | 6 |
|
|
74
|
+
| [#1000068](https://cloudbongos.com/builders#/goal/1000068) — Government, not governance | **becomes the area** — Lars blitzes it; bugs and a rename | 7 |
|
|
75
|
+
| [#1000057](https://cloudbongos.com/builders#/goal/1000057) — Rank blocker matrix | fold into government | 7 |
|
|
76
|
+
| [#1000078](https://cloudbongos.com/builders#/goal/1000078) — Project money out | merge into builder compensation | 8 |
|
|
77
|
+
| [#1000067](https://cloudbongos.com/builders#/goal/1000067) — Ideator compensation | merge into builder payout | 8 |
|
|
78
|
+
| [#1000084](https://cloudbongos.com/builders#/goal/1000084) — Execution integrity & tenant isolation | bring into the security goal | 10 |
|
|
79
|
+
| [#1000070](https://cloudbongos.com/builders#/goal/1000070) — Scan-before-install | fold — Rini does the bug task | 10 |
|
|
80
|
+
|
|
81
|
+
Areas **3** (human project management), **4** (core distribution), **5** (module
|
|
82
|
+
distribution & economy), **8** (credit economy), **9** (platform analytics) and
|
|
83
|
+
**10** (security) have no single existing goal to promote and are **created** by
|
|
84
|
+
R29, taking the folds listed against them.
|
|
85
|
+
|
|
86
|
+
### Finish, then close
|
|
87
|
+
|
|
88
|
+
| Goal | Disposition |
|
|
89
|
+
|---|---|
|
|
90
|
+
| [#1000086](https://cloudbongos.com/builders#/goal/1000086) — Strict versioning | *"EVERYONE DO THIS NOW."* Finish, then close — it is the enabling work for the cut itself. |
|
|
91
|
+
| [#1000073](https://cloudbongos.com/builders#/goal/1000073) — Finish the GDS→Bongos rename | Niels finishes; small. |
|
|
92
|
+
| [#1000082](https://cloudbongos.com/builders#/goal/1000082) — Status ledger in the world | Lars deletes the status page, then closes it out. |
|
|
93
|
+
| [#1000024](https://cloudbongos.com/builders#/goal/1000024) — Build-pipeline integrity | A pile of bugs — check relevance, then fold or close. |
|
|
94
|
+
| [#1000032](https://cloudbongos.com/builders#/goal/1000032) — Instance hosting + domains | Clean out, then close. |
|
|
95
|
+
| [#1000058](https://cloudbongos.com/builders#/goal/1000058) — Non-cloudbongos project lifecycle | All bugs — fold into another goal. |
|
|
96
|
+
|
|
97
|
+
### Deleted
|
|
98
|
+
|
|
99
|
+
Twelve goals are cut outright:
|
|
100
|
+
|
|
101
|
+
[#1000037](https://cloudbongos.com/builders#/goal/1000037) Pay-on-land incentive alignment ·
|
|
102
|
+
[#1000043](https://cloudbongos.com/builders#/goal/1000043) Platform security: safe multi-tenant hosting ·
|
|
103
|
+
[#1000044](https://cloudbongos.com/builders#/goal/1000044) Living project sky ·
|
|
104
|
+
[#1000047](https://cloudbongos.com/builders#/goal/1000047) Project pricing & hosting revenue ·
|
|
105
|
+
[#1000048](https://cloudbongos.com/builders#/goal/1000048) SEO & public discoverability ·
|
|
106
|
+
[#1000049](https://cloudbongos.com/builders#/goal/1000049) Orb Physics ·
|
|
107
|
+
[#1000050](https://cloudbongos.com/builders#/goal/1000050) Self-hosted project analytics ·
|
|
108
|
+
[#1000052](https://cloudbongos.com/builders#/goal/1000052) First-run onboarding ·
|
|
109
|
+
[#1000053](https://cloudbongos.com/builders#/goal/1000053) Sessions start knowing ·
|
|
110
|
+
[#1000063](https://cloudbongos.com/builders#/goal/1000063) The front door ·
|
|
111
|
+
[#1000064](https://cloudbongos.com/builders#/goal/1000064) The hall as the room ·
|
|
112
|
+
[#4](https://cloudbongos.com/builders#/goal/4) CB-V1 — general (the old general bucket).
|
|
113
|
+
|
|
114
|
+
**"Delete" means `archived`, not a row removal.** `goals.status` is
|
|
115
|
+
`open|achieved|archived` (migration 160) and nothing deletes a goal — archiving is
|
|
116
|
+
the disposition, and it is reversible via `POST /goals/:id/reopen`. Preserving the
|
|
117
|
+
rows is deliberate: shipped tasks under a cut goal keep their attribution and
|
|
118
|
+
credit history, which a delete would strand.
|
|
119
|
+
|
|
120
|
+
## 4. The consequence that reorders the goal: R14 is the keystone
|
|
121
|
+
|
|
122
|
+
**None of section 3 can be executed today, and the reason is a rule this goal
|
|
123
|
+
shipped four tasks ago.**
|
|
124
|
+
|
|
125
|
+
R10 ([task #1003597](https://cloudbongos.com/builders#/task/1003597)) made
|
|
126
|
+
`POST /goals/:id/archive` refuse a goal that still holds unfinished tasks, returning
|
|
127
|
+
the tasks and demanding a disposition. That was correct and is exactly ADR 0250 D3.
|
|
128
|
+
But **the vector that supplies a disposition is R14** ([task #1003601](https://cloudbongos.com/builders#/task/1003601)), which has not shipped. So right now the
|
|
129
|
+
archive door is closed and the key has not been cut.
|
|
130
|
+
|
|
131
|
+
Every one of the twelve deletes and every fold in section 3 goes through that
|
|
132
|
+
door. That makes R14 the **keystone of the entire cut**, not one feature among
|
|
133
|
+
thirteen, and it should be built before the rest of the version-close chain
|
|
134
|
+
rather than in rank order.
|
|
135
|
+
|
|
136
|
+
This is worth stating plainly because it looks like a bug and is not: the refusal
|
|
137
|
+
is the system working as designed, one task ahead of its own remedy.
|
|
138
|
+
|
|
139
|
+
## 5. What this does not decide
|
|
140
|
+
|
|
141
|
+
- **It does not renumber or re-rank the ten areas.** The order in §1 is the review's order, not a priority.
|
|
142
|
+
- **It does not set criteria for the six new goals.** R29 scopes them; an area without done-when criteria cannot close, and inventing criteria for someone else's area is how a goal acquires scope its owner never agreed to.
|
|
143
|
+
- **It does not move any task.** Rehoming is R25 (the catch-alls) and R26 (the closed goals holding open work).
|
|
144
|
+
- **It does not decide the three open questions in §3.** They are surfaced, not guessed.
|
package/docs/adr/README.md
CHANGED
|
@@ -355,3 +355,4 @@ This keeps the decision history honest and traceable.
|
|
|
355
355
|
| 0261 | [**A preselect always carries a reason; the bundle’s summary is the floor** ([task 1003684](https://cloudbongos.com/builders#/task/1003684) · goal 1000046 — *Project creation*). Resolves a collision between two rules that were each right alone. [ADR 0243](<redacted>.md) made `adjustments` a **delta** — a team-shape rule that fires without moving anything claims nothing, because a sentence explaining a change that did not happen is a claim the owner cannot check. [ADR 0237](<redacted>.md) says a preselect the owner cannot see a reason for is one they must audit, which is worse than no preselect at all. The two collide whenever a type’s bundle **already contains** what a rule would add: `game` is `[dev-box, discord]` and the `small-team` rule adds `dev-box`, so the rule fires, moves nothing, and correctly reports `adjustments: []`. Confirmed on live core 1.19.580 — `byTeamShape[small-team]` = `{"bundle":["dev-box","discord"],"adjustments":[]}`. The owner then reached step 4 and saw **two extras switched on with no reason beside them**, `#modWhy` an empty hidden div: exactly what 0237 exists to prevent, produced by 0243 behaving correctly. Filed as a dropped adjustment; it was not — the engine is right, and the panel simply had ONE voice and fell silent when the rule had nothing to say. **Decision: the why-line has two voices and the type’s is the floor.** A real adjustment still speaks for itself and is never displaced; when none moved, the type’s own curated `summary` explains the preselect; when the owner has answered the picker themselves the panel stays silent (unchanged — a reason handed back for a toggle they just flipped reads as the panel arguing with them); a bundle with no summary still says nothing, because a floor is not an invention. **No new copy, endpoint or field** — the summary was authored for this job in `starter-bundles.js`, served on every row of `GET /provisioning/starter-bundles`, and rendered NOWHERE in the product until now. 0243 is not weakened: the delta stays a delta and no rule is credited with a change it did not make. The pin `tests/projects_hub_module_picker.mjs` carried a fixture with **no `summary` field**, which made four “no adjustment, no sentence” cases pass for the wrong reason; the fixture now carries the real summaries and those cases assert the new contract, including explicitly that no RULE sentence is invented for a change that did not happen. Rejected: making the rule claim a no-op change (re-introduces the unverifiable claim 0243 removed); widening the bundles so no rule is ever redundant (contorts curated presets for a rendering concern, and the redundancy returns on the next edit); and leaving it silent, declined by the owner once the trade-off was put to them directly.](<redacted>.md) | provisioning / starter bundles / owner-facing copy |
|
|
356
356
|
| 0262 | [**A bug never lands in the inbox: the system names the home the reporter didn't** ([task 1003691](https://cloudbongos.com/builders#/task/1003691) · goal 1000086 — *Strict versioning*). `routing.js` returned early on a goal-less filing, so its own `kind='bug'` hard-land branch was UNREACHABLE without a goal — `kind` only ever chose a routed task's STATUS, never whether it routed — and a reported defect sat in `idea_inbox` awaiting the triage pass criterion C1 exists to remove. The premise that [ADR 0250](<redacted>.md) D4 and [ADR 0235](<redacted>.md) contradict each other is FALSE: 0235's exemption is an advisory silence in `goal-advisory.adviseGoal`, and its actual decision already requires a real `goal_id`. So nothing is amended; this is the explicit decision 0235 §6 said would be needed. **D1** a homeless bug gets the version's MAINTENANCE goal (R18's design), find-or-created under an advisory lock inside the route's own transaction — not the catch-all R11 is deleting, and adding no `allowCatchAll` caller. **D2** the gate is the VECTOR (`api`, `discord-bugs`), not the kind: `#ideas` passes no kind, so the classifier guesses one from keywords, and gating on kind alone would silently overturn [ADR 0234](<redacted>.md)'s owner-interview decision. Rejected: goal-less bug tasks (breaks D4 a day after it landed); rejecting the filing (Discord cannot retry, so the report is lost); the catch-all (repopulates the bucket being drained); building R18 whole first.](<redacted>.md) | work intake / idea routing / the version-goal invariant |
|
|
357
357
|
| 0263 | [**How a version closes: auto, early, roll-forward, and the maintenance exemption** ([task 1003593](https://cloudbongos.com/builders#/task/1003593) · goal 1000086 — *Strict versioning*). [ADR 0250](<redacted>.md) D5 decided a version *can* close; this is the design pass that makes its five build tasks mechanical, so they do not each invent their own refusal codes and payload shapes and meet only in production. **The asymmetry that shrinks the feature:** auto-close fires when the last non-maintenance goal achieves — so by construction every non-maintenance goal is already closed and the set needing a disposition is EMPTY. All the machinery (the disposition map, the refusal that returns the goals, successor lineage) belongs to EARLY close alone; R16 is small and R12 carries the weight. **Auto-close hangs off BOTH achievement writers** (`db-goals.achieveGoalIfComplete` and the `done-when` cascade — the deliberate duplicate D2 already governs, a require cycle being the reason they are not one helper), on the caller `exec` so it sees the ship's uncommitted flip, and **may never fail a ship**: wrapped and swallowed like `closeCompletedWorkForShip`, idempotent under `WHERE status='building'`, so a failure leaves a version un-closed (self-healing on the next reconciler sweep) and never half-closed. **Early close** is the R10 archive two-step verbatim in spirit — refuse with `version_holds_open_goals` **returning the goals**, then proceed with a per-goal `roll_forward`/`abandon` map and a required `reason`; six named codes, because the callers are agents. **Two briefs are corrected by measurement:** the lineage column is `succeeded_by_goal_id` (migration 160, on the OLD row pointing forward) — not the `succeeds_goal_id` both 0250 and R17's brief name, so R17 needs NO migration; and R18's `is_maintenance` flag is load-bearing rather than cosmetic, because answering the close count with a `title NOT LIKE` would let a hand-titled goal silently exempt itself from the gate that decides when a VERSION closes. Promotion runs inside the close txn and BEFORE roll-forward (`ensureMaintenanceGoal` returns null for a non-`building` version, so a carry-over run first silently carries nothing); zero planning versions is not an error; R19's one migration should carry the `building` AND `planning` partial unique indexes so the promotion race resolves like a 23505 rather than a prayer. Rejected: auto-creating the successor version inside a close (the "fake hotfix version" escape hatch 0250 built the override counter to prevent), writing `limitations/<version>-shipped.md` from the route (file I/O in a ship transaction — it stays R23's CLI work), and a version reopen.](<redacted>.md) | version lifecycle / scope closure |
|
|
358
|
+
| 0264 | [**The ten working areas: one goal per area, held until 5,000 builders** ([task 1003696](https://cloudbongos.com/builders#/task/1003696) · goal 1000086 — *Strict versioning*, criterion `sv-cutover-proven-live`). The owner and the full team ran a manual goal review on 2026-09-07 and cut **38 open goals down to 10**, one per working area, each with a named owner and a described 5,000-builder end state: project creation (Rini), account/community (Rini), human project management (Scott), core distribution (Lars/Will), module distribution & economy (Will), the four-role experience incl. agents (Masterqua/Rini), government (Will/Masterqua), the credit economy (Scott), platform analytics (Lars), security (everyone bar Nils and Scott). **Why the rules were not enough on their own:** [ADR 0250](<redacted>.md) D1 closes the goal set of a BUILDING version — which freezes 38 goals exactly as effectively as it would freeze 10. Enforcement cannot shrink a scope that is already too large, so the rules and the cut are two halves of one move; the horizon is a SCALE (5,000 builders), not a date, because that is the question that decides when this set stops being the right set. **The finding that reorders the goal:** none of the cut can be executed today. R10 ([task 1003597](https://cloudbongos.com/builders#/task/1003597)) made `POST /goals/:id/archive` refuse a goal holding unfinished tasks and demand a disposition — correct, and exactly D3 — but the vector that SUPPLIES a disposition is R14 ([task 1003601](https://cloudbongos.com/builders#/task/1003601)), unshipped. Every one of the 12 deletes and every fold goes through that door, so **R14 is the keystone of the whole cut**, not one feature among thirteen, and is built before the rest of the version-close chain rather than in rank order. It looks like a bug and is not: the refusal is the design, landed one task ahead of its own remedy. "Delete" means `archived`, never a row removal (`goals.status` is open|achieved|archived, migration 160; reversible via `/reopen`) — preserving the rows keeps shipped-task attribution and credit history a delete would strand. Three open goals were NOT covered by the review and are surfaced as open questions rather than guessed: 1000003 (the catch-all — already R25's target), 1000065 (assumed to be area 2's community half), and 1000088 (repo cleanup — maps to no area, needs an owner decision). Deliberately does not decide: the areas' priority order, criteria for the six goals R29 must create (inventing criteria for someone else's area is how a goal acquires scope its owner never agreed to), or any task rehoming (R25/R26).](<redacted>.md) | scope / goal set / owner decision |
|
package/docs/api/openapi.json
CHANGED
|
@@ -5139,6 +5139,17 @@
|
|
|
5139
5139
|
"description": "Path parameter `id`."
|
|
5140
5140
|
}
|
|
5141
5141
|
],
|
|
5142
|
+
"requestBody": {
|
|
5143
|
+
"required": false,
|
|
5144
|
+
"content": {
|
|
5145
|
+
"application/json": {
|
|
5146
|
+
"schema": {
|
|
5147
|
+
"$ref": "#/components/schemas/PostGoalsIdArchiveRequest"
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
},
|
|
5151
|
+
"x-validated": true
|
|
5152
|
+
},
|
|
5142
5153
|
"responses": {
|
|
5143
5154
|
"200": {
|
|
5144
5155
|
"description": "Success.",
|
|
@@ -5151,7 +5162,7 @@
|
|
|
5151
5162
|
}
|
|
5152
5163
|
},
|
|
5153
5164
|
"400": {
|
|
5154
|
-
"$ref": "#/components/responses/
|
|
5165
|
+
"$ref": "#/components/responses/ValidationFailed"
|
|
5155
5166
|
},
|
|
5156
5167
|
"401": {
|
|
5157
5168
|
"$ref": "#/components/responses/Unauthorized"
|
|
@@ -19912,17 +19923,32 @@
|
|
|
19912
19923
|
"repo"
|
|
19913
19924
|
]
|
|
19914
19925
|
},
|
|
19926
|
+
"PostGoalsIdArchiveRequest": {
|
|
19927
|
+
"type": "object",
|
|
19928
|
+
"properties": {
|
|
19929
|
+
"reason": {
|
|
19930
|
+
"type": "string",
|
|
19931
|
+
"maxLength": 2000
|
|
19932
|
+
},
|
|
19933
|
+
"dispositions": {
|
|
19934
|
+
"type": "object"
|
|
19935
|
+
}
|
|
19936
|
+
},
|
|
19937
|
+
"additionalProperties": false
|
|
19938
|
+
},
|
|
19915
19939
|
"PostGoalsIdArchiveResponse": {
|
|
19916
19940
|
"type": "object",
|
|
19917
19941
|
"properties": {
|
|
19918
19942
|
"ok": {
|
|
19919
19943
|
"type": "boolean"
|
|
19920
19944
|
},
|
|
19921
|
-
"goal": {}
|
|
19945
|
+
"goal": {},
|
|
19946
|
+
"applied": {}
|
|
19922
19947
|
},
|
|
19923
19948
|
"required": [
|
|
19924
19949
|
"ok",
|
|
19925
|
-
"goal"
|
|
19950
|
+
"goal",
|
|
19951
|
+
"applied"
|
|
19926
19952
|
]
|
|
19927
19953
|
},
|
|
19928
19954
|
"PostGoalsIdConflictsResolveRequest": {
|
|
@@ -22676,7 +22702,7 @@
|
|
|
22676
22702
|
}
|
|
22677
22703
|
},
|
|
22678
22704
|
"x-endpoint-count": 359,
|
|
22679
|
-
"x-schema-count":
|
|
22705
|
+
"x-schema-count": 396,
|
|
22680
22706
|
"x-undocumented-bodies": 9,
|
|
22681
22707
|
"x-response-schemas": 267,
|
|
22682
22708
|
"x-generated-by": "scripts/gds/gen-api-docs.js"
|
package/docs/api-reference.md
CHANGED
|
@@ -230,7 +230,7 @@ Base path: `/api/bongos`. Ranks (enforced server-side, [ADR 0016](adr/<redacted>
|
|
|
230
230
|
| GET | `/api/bongos/goals/:id` | `any-builder` | — | GET /goals/:id — one goal with its members + linked criteria (read). |
|
|
231
231
|
| PATCH | `/api/bongos/goals/:id` | `any-builder` | — | PATCH /goals/:id — correct a goal's own title/subtitle/description (tasks 1003102, 1003224). |
|
|
232
232
|
| PATCH | `/api/bongos/goals/:id/accepting-requests` | `any-builder` | — | PATCH /goals/:id/accepting-requests { accepting_requests } — a private goal's lead closes or re-opens it to join-requests (task 1002971, … |
|
|
233
|
-
| POST | `/api/bongos/goals/:id/archive` | `metic+archon` |
|
|
233
|
+
| POST | `/api/bongos/goals/:id/archive` | `metic+archon` | `reason`, `dispositions` | POST /goals/:id/archive — retire a goal (open\|achieved → archived). |
|
|
234
234
|
| PATCH | `/api/bongos/goals/:id/category` | `any-builder` | — | PATCH /goals/:id/category — body { category_id } (null clears it). |
|
|
235
235
|
| GET | `/api/bongos/goals/:id/conflicts` | `any-builder` | — | GET /goals/:id/conflicts — the server-side task-conflict view (task 1736). |
|
|
236
236
|
| POST | `/api/bongos/goals/:id/conflicts/resolve` | `any-builder` | `action`, `note`, `touches`, `first_task_id`, `then_task_id`, `task_id` | POST /goals/:id/conflicts/resolve — the owner/manager resolution actions for a detected conflict (task 1736; authority model ADR 0106): s… |
|
|
@@ -1625,5 +1625,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1625
1625
|
landed since 1.19.586 with no explicit bump. run 34172785860. (task 1002620)
|
|
1626
1626
|
1.19.588 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1627
1627
|
landed since 1.19.587 with no explicit bump. run 34178696372. (task 1002620)
|
|
1628
|
+
1.19.589 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1629
|
+
landed since 1.19.588 with no explicit bump. run 34179479729. (task 1002620)
|
|
1630
|
+
1.19.590 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1631
|
+
landed since 1.19.589 with no explicit bump. run 34180363432. (task 1002620)
|
|
1628
1632
|
---------------------------------------------------------------------------
|
|
1629
1633
|
```
|
|
@@ -14,7 +14,7 @@ const api = require('../../src/module-api');
|
|
|
14
14
|
const { pool } = api;
|
|
15
15
|
const doneWhen = require('./done-when.js');
|
|
16
16
|
const { deriveRequiredRank, highestRank } = require('./db-rank-authz.js');
|
|
17
|
-
const { nonTerminalSql } = require('./task-status');
|
|
17
|
+
const { nonTerminalSql, isTerminalStatus } = require('./task-status');
|
|
18
18
|
const { maintenanceGoalTitle } = require('./goal-advisory.js');
|
|
19
19
|
const { SMOKE_NOT_LIKE } = require('./db-shared.js');
|
|
20
20
|
|
|
@@ -487,6 +487,130 @@ async function openTasksInGoal(goalId, { cap = 50 } = {}, deps = {}) {
|
|
|
487
487
|
);
|
|
488
488
|
return { total: rows[0]?.total ?? 0, tasks: rows.map(({ total, ...t }) => t) };
|
|
489
489
|
}
|
|
490
|
+
// archiveGoalWithDispositions — apply the caller's decision about each unfinished
|
|
491
|
+
// task, then archive the goal, in ONE transaction (BV1.R14, task 1003601, goal
|
|
492
|
+
// 1000086, ADR 0250 D3). The write half of the archive two-step whose refusal
|
|
493
|
+
// R10 (task 1003597) built and whose plan `planGoalArchiveDispositions` validates.
|
|
494
|
+
//
|
|
495
|
+
// ATOMIC IS THE WHOLE POINT, not a nicety. The failure this replaces is a goal
|
|
496
|
+
// reported closed while its work is still open; a partially-applied disposition
|
|
497
|
+
// produces the mirror of it — some tasks abandoned, some moved, the goal still
|
|
498
|
+
// open — and leaves the caller unable to tell from outside which half ran. One
|
|
499
|
+
// transaction means the call is safe to retry verbatim after any error.
|
|
500
|
+
//
|
|
501
|
+
// The goal row is locked FOR UPDATE first, so a concurrent create-task-in-goal
|
|
502
|
+
// cannot slip a new task in between the plan being validated and the archive
|
|
503
|
+
// landing. The final re-count inside the same transaction is the belt to that
|
|
504
|
+
// braces: it asserts the invariant the whole goal exists to protect (an archived
|
|
505
|
+
// goal holds no non-terminal work) against the committed state rather than
|
|
506
|
+
// against the plan, and rolls back if it does not hold. Cheap, and it is the one
|
|
507
|
+
// check that cannot be fooled by a bug in the planner.
|
|
508
|
+
//
|
|
509
|
+
// `plan` is the validated output of planGoalArchiveDispositions: [{ taskId, verb,
|
|
510
|
+
// toGoalId }]. Move targets are resolved and checked open BY THE ROUTE via
|
|
511
|
+
// goalNotOpenRefusal — the ONE definition of "this goal takes no new work"
|
|
512
|
+
// (BV1.R05) — and are re-read here under lock only to guard the interval between
|
|
513
|
+
// that check and this write.
|
|
514
|
+
//
|
|
515
|
+
// ABANDON MIRRORS abandonTask (db-ship.js) rather than calling it: that function
|
|
516
|
+
// opens its own connection and commits, which cannot join this transaction. The
|
|
517
|
+
// two behaviours kept identical on purpose are the `value_summary` stamp
|
|
518
|
+
// (`Abandoned: <reason>`) and the refusal on an active claim — a task someone is
|
|
519
|
+
// actively working must not be abandoned out from under them by a goal archive.
|
|
520
|
+
async function archiveGoalWithDispositions({ goalId, plan = [], reason }, deps = {}) {
|
|
521
|
+
const activePool = (deps.pool && process.env.NODE_ENV === 'test') ? deps.pool : pool;
|
|
522
|
+
const client = await activePool.connect();
|
|
523
|
+
try {
|
|
524
|
+
await client.query('BEGIN');
|
|
525
|
+
|
|
526
|
+
const { rows: goalRows } = await client.query(
|
|
527
|
+
`SELECT ${GOAL_COLS} FROM goals WHERE id = $1 FOR UPDATE`, [goalId]
|
|
528
|
+
);
|
|
529
|
+
if (!goalRows[0]) throw Object.assign(new Error('goal not found'), { code: 'GOAL_NOT_FOUND' });
|
|
530
|
+
if (goalRows[0].status === 'archived') {
|
|
531
|
+
await client.query('ROLLBACK');
|
|
532
|
+
return { goal: goalRows[0], applied: [], alreadyArchived: true };
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const applied = [];
|
|
536
|
+
for (const step of plan) {
|
|
537
|
+
const { rows: tRows } = await client.query(
|
|
538
|
+
`SELECT id, title, status FROM tasks WHERE id = $1 AND goal_id = $2 FOR UPDATE`,
|
|
539
|
+
[step.taskId, goalId]
|
|
540
|
+
);
|
|
541
|
+
// The task left this goal (or shipped) while the caller was deciding. Not an
|
|
542
|
+
// error: the disposition is moot, and the final re-count is what actually
|
|
543
|
+
// guards the invariant. Recorded as 'skipped' so the response tells the
|
|
544
|
+
// truth about what this call did rather than claiming a write it never made.
|
|
545
|
+
if (!tRows[0]) { applied.push({ task_id: String(step.taskId), verb: step.verb, result: 'skipped_not_in_goal' }); continue; }
|
|
546
|
+
if (isTerminalStatus(tRows[0].status)) {
|
|
547
|
+
applied.push({ task_id: String(step.taskId), verb: step.verb, result: 'skipped_already_terminal', status: tRows[0].status });
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
const { rows: claimed } = await client.query(
|
|
552
|
+
`SELECT id FROM claims WHERE task_id = $1 AND released_at IS NULL LIMIT 1`, [step.taskId]
|
|
553
|
+
);
|
|
554
|
+
if (claimed[0]) {
|
|
555
|
+
throw Object.assign(new Error('task has an active claim'), {
|
|
556
|
+
code: 'TASK_HAS_ACTIVE_CLAIM', taskId: String(step.taskId), taskTitle: tRows[0].title,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (step.verb === 'abandon') {
|
|
561
|
+
await client.query(
|
|
562
|
+
`UPDATE tasks SET status = 'abandoned',
|
|
563
|
+
value_summary = COALESCE(NULLIF($2, ''), value_summary),
|
|
564
|
+
updated_at = now()
|
|
565
|
+
WHERE id = $1`,
|
|
566
|
+
[step.taskId, `Abandoned: ${reason}`]
|
|
567
|
+
);
|
|
568
|
+
applied.push({ task_id: String(step.taskId), verb: 'abandon', result: 'abandoned' });
|
|
569
|
+
} else {
|
|
570
|
+
const { rows: dest } = await client.query(
|
|
571
|
+
`SELECT id, status FROM goals WHERE id = $1 FOR SHARE`, [step.toGoalId]
|
|
572
|
+
);
|
|
573
|
+
if (!dest[0]) {
|
|
574
|
+
throw Object.assign(new Error('move target not found'), { code: 'MOVE_TARGET_NOT_FOUND', toGoalId: String(step.toGoalId) });
|
|
575
|
+
}
|
|
576
|
+
if (dest[0].status !== 'open') {
|
|
577
|
+
throw Object.assign(new Error('move target is not open'), {
|
|
578
|
+
code: 'MOVE_TARGET_NOT_OPEN', toGoalId: String(step.toGoalId), targetStatus: dest[0].status,
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
await client.query(`UPDATE tasks SET goal_id = $2, updated_at = now() WHERE id = $1`, [step.taskId, step.toGoalId]);
|
|
582
|
+
applied.push({ task_id: String(step.taskId), verb: 'move', result: 'moved', to_goal_id: String(step.toGoalId) });
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// The invariant, asserted against committed-in-this-txn state rather than
|
|
587
|
+
// against the plan. If anything is still non-terminal here, the archive would
|
|
588
|
+
// recreate exactly the condition goal 1000086 exists to make impossible.
|
|
589
|
+
const { rows: left } = await client.query(
|
|
590
|
+
`SELECT count(*)::int AS n FROM tasks t
|
|
591
|
+
WHERE t.goal_id = $1 AND ${nonTerminalSql()} AND ${SMOKE_NOT_LIKE.replace(/title/, 't.title')}`,
|
|
592
|
+
[goalId]
|
|
593
|
+
);
|
|
594
|
+
if ((left[0]?.n ?? 0) > 0) {
|
|
595
|
+
throw Object.assign(new Error('goal still holds unfinished tasks after dispositions'), {
|
|
596
|
+
code: 'DISPOSITION_INCOMPLETE_AFTER_APPLY', remaining: left[0].n,
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
const { rows: archived } = await client.query(
|
|
601
|
+
`UPDATE goals SET status = 'archived', updated_at = now() WHERE id = $1 RETURNING ${GOAL_COLS}`,
|
|
602
|
+
[goalId]
|
|
603
|
+
);
|
|
604
|
+
await client.query('COMMIT');
|
|
605
|
+
return { goal: archived[0], applied, alreadyArchived: false };
|
|
606
|
+
} catch (err) {
|
|
607
|
+
try { await client.query('ROLLBACK'); } catch { /* the original error is the one that matters */ }
|
|
608
|
+
throw err;
|
|
609
|
+
} finally {
|
|
610
|
+
client.release();
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
490
614
|
async function listCriteriaForGoal(goalId, deps = {}) {
|
|
491
615
|
const activePool = (deps.pool && process.env.NODE_ENV === 'test') ? deps.pool : pool;
|
|
492
616
|
// task 1002164: carry the linked-task counts so the caller can derive the
|
|
@@ -937,6 +1061,7 @@ module.exports = {
|
|
|
937
1061
|
acceptMembershipRequestAndAddMember,
|
|
938
1062
|
ensureMaintenanceGoal,
|
|
939
1063
|
achieveGoalIfComplete,
|
|
1064
|
+
archiveGoalWithDispositions,
|
|
940
1065
|
addGoalMember,
|
|
941
1066
|
addGoalScopeModules,
|
|
942
1067
|
createGoal,
|
package/modules/lifecycle/db.js
CHANGED
|
@@ -81,6 +81,7 @@ const {
|
|
|
81
81
|
isGoalMember,
|
|
82
82
|
listActiveClaimsForTasks,
|
|
83
83
|
listCriteriaForGoal,
|
|
84
|
+
archiveGoalWithDispositions,
|
|
84
85
|
openTasksInGoal,
|
|
85
86
|
listGoalMembers,
|
|
86
87
|
listGoals,
|
|
@@ -252,6 +253,7 @@ module.exports = {
|
|
|
252
253
|
listInboxFor,
|
|
253
254
|
listPendingRequestsForGoal,
|
|
254
255
|
listCriteriaForGoal,
|
|
256
|
+
archiveGoalWithDispositions,
|
|
255
257
|
openTasksInGoal,
|
|
256
258
|
createTask,
|
|
257
259
|
generalGoalIdForVersion,
|