@bongos/core 1.19.499 → 1.19.501
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 -26
- package/clients/bongos-client/index.d.ts +1 -1
- package/docs/adr/0234-idea-routing-capture-time-promotion-landing-matrix-homeless-inbox.md +9 -7
- package/docs/adr/0235-a-fix-category-is-kindbug-under-a-goal-no-column-required.md +131 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +4 -2
- package/docs/copy-inventory.md +51 -51
- package/docs/copy-registry.json +57 -57
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/goals-render.js +14 -3
- package/modules/lifecycle/db-deps-criteria.js +23 -0
- package/modules/lifecycle/db.js +2 -0
- package/modules/lifecycle/goal-advisory.js +80 -22
- package/modules/lifecycle/routes/goals.js +9 -2
- package/modules/lifecycle/routes/task-write-routes.js +7 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/goal_advisory.mjs +115 -4
- package/tests/goal_criterion_confirm_ui.mjs +32 -2
- package/tests/goal_routes.mjs +29 -1
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.501",
|
|
6
|
+
"core_contract": "1.19.501",
|
|
7
|
+
"source_commit": "d6bac950eb66f758928e815cf88f2d6ddacaf30a",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-03T15:41:46.721Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 426,
|
|
13
13
|
"agent_docs_stubbed": 24,
|
|
14
14
|
"functional_verbatim": 1942,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2392,
|
|
20
|
+
"tree_sha256": "d4ef43893ca56f9d2d4e0df1823f7fdb2692bb3f3a18d8bb2fece9221dc83925",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -382,7 +382,7 @@
|
|
|
382
382
|
{
|
|
383
383
|
"path": "clients/bongos-client/index.d.ts",
|
|
384
384
|
"mode": "0000644",
|
|
385
|
-
"sha256": "
|
|
385
|
+
"sha256": "036e1064bb9513d7acbf0dafd0850deac9f45a38f31c3cbb8c1d7493c9159b76"
|
|
386
386
|
},
|
|
387
387
|
{
|
|
388
388
|
"path": "clients/bongos-client/index.mjs",
|
|
@@ -1677,12 +1677,17 @@
|
|
|
1677
1677
|
{
|
|
1678
1678
|
"path": "docs/adr/0234-idea-routing-capture-time-promotion-landing-matrix-homeless-inbox.md",
|
|
1679
1679
|
"mode": "0000644",
|
|
1680
|
-
"sha256": "
|
|
1680
|
+
"sha256": "417d1faffdc644b6dbad3d27fc58fac7d87609550060d0cd4edb0bad702d6b4c"
|
|
1681
|
+
},
|
|
1682
|
+
{
|
|
1683
|
+
"path": "docs/adr/0235-a-fix-category-is-kindbug-under-a-goal-no-column-required.md",
|
|
1684
|
+
"mode": "0000644",
|
|
1685
|
+
"sha256": "f3573d82c77dc2b7d006f4ee8051b9b5a247669d2ac4c046c2de7c282d4e65ff"
|
|
1681
1686
|
},
|
|
1682
1687
|
{
|
|
1683
1688
|
"path": "docs/adr/README.md",
|
|
1684
1689
|
"mode": "0000644",
|
|
1685
|
-
"sha256": "
|
|
1690
|
+
"sha256": "43d782f6ca3f187f280e13466830ceceaf6ba74da2cc669e8b7dd6ece166d657"
|
|
1686
1691
|
},
|
|
1687
1692
|
{
|
|
1688
1693
|
"path": "docs/api-reference.md",
|
|
@@ -1692,7 +1697,7 @@
|
|
|
1692
1697
|
{
|
|
1693
1698
|
"path": "docs/api/openapi.json",
|
|
1694
1699
|
"mode": "0000644",
|
|
1695
|
-
"sha256": "
|
|
1700
|
+
"sha256": "c02241e6fd8372fb80339eaa385f227b642754ff127dd529cfdc94ed87d077a4"
|
|
1696
1701
|
},
|
|
1697
1702
|
{
|
|
1698
1703
|
"path": "docs/architecture.md",
|
|
@@ -1717,12 +1722,12 @@
|
|
|
1717
1722
|
{
|
|
1718
1723
|
"path": "docs/copy-inventory.md",
|
|
1719
1724
|
"mode": "0000644",
|
|
1720
|
-
"sha256": "
|
|
1725
|
+
"sha256": "a58679c1cbf659e334938c752ff1c5dd56800057fe590b74a65ac6c5799900ab"
|
|
1721
1726
|
},
|
|
1722
1727
|
{
|
|
1723
1728
|
"path": "docs/copy-registry.json",
|
|
1724
1729
|
"mode": "0000644",
|
|
1725
|
-
"sha256": "
|
|
1730
|
+
"sha256": "acb0ebb7c6c6204c482a4f7e57ee8e7187b1b03441abe5110a36dd7a159f17d5"
|
|
1726
1731
|
},
|
|
1727
1732
|
{
|
|
1728
1733
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2567,7 +2572,7 @@
|
|
|
2567
2572
|
{
|
|
2568
2573
|
"path": "docs/module-api-changelog.md",
|
|
2569
2574
|
"mode": "0000644",
|
|
2570
|
-
"sha256": "
|
|
2575
|
+
"sha256": "49c673ec2ee31c6174bdc48d1b124905bd1f47a51db4e0a3a75e4d937a25ba0a"
|
|
2571
2576
|
},
|
|
2572
2577
|
{
|
|
2573
2578
|
"path": "docs/modules-contract.md",
|
|
@@ -4642,7 +4647,7 @@
|
|
|
4642
4647
|
{
|
|
4643
4648
|
"path": "modules/hall-ui/public/goals-render.js",
|
|
4644
4649
|
"mode": "0000644",
|
|
4645
|
-
"sha256": "
|
|
4650
|
+
"sha256": "b494bec38dc6eaa4812a59b6405e7bfd2cc58688d3725b095956a4e1f7e0b4b7"
|
|
4646
4651
|
},
|
|
4647
4652
|
{
|
|
4648
4653
|
"path": "modules/hall-ui/public/goals.css",
|
|
@@ -5242,7 +5247,7 @@
|
|
|
5242
5247
|
{
|
|
5243
5248
|
"path": "modules/lifecycle/db-deps-criteria.js",
|
|
5244
5249
|
"mode": "0000644",
|
|
5245
|
-
"sha256": "
|
|
5250
|
+
"sha256": "04406d383068e42eafed42fa51cf9108df2907f0807b1ed7bcd0abc9eaf7bfb7"
|
|
5246
5251
|
},
|
|
5247
5252
|
{
|
|
5248
5253
|
"path": "modules/lifecycle/db-goals.js",
|
|
@@ -5287,7 +5292,7 @@
|
|
|
5287
5292
|
{
|
|
5288
5293
|
"path": "modules/lifecycle/db.js",
|
|
5289
5294
|
"mode": "0000644",
|
|
5290
|
-
"sha256": "
|
|
5295
|
+
"sha256": "09d40dd2eaae47c4620993ad3db6a6db2d880cc499724d31ceff29604960fd64"
|
|
5291
5296
|
},
|
|
5292
5297
|
{
|
|
5293
5298
|
"path": "modules/lifecycle/dead-deps.js",
|
|
@@ -5312,7 +5317,7 @@
|
|
|
5312
5317
|
{
|
|
5313
5318
|
"path": "modules/lifecycle/goal-advisory.js",
|
|
5314
5319
|
"mode": "0000644",
|
|
5315
|
-
"sha256": "
|
|
5320
|
+
"sha256": "b9138fa63ef324b9353354e4c5398150d576dd7e3547bb6889f2f83ebd7b995f"
|
|
5316
5321
|
},
|
|
5317
5322
|
{
|
|
5318
5323
|
"path": "modules/lifecycle/goal-authz.js",
|
|
@@ -5457,7 +5462,7 @@
|
|
|
5457
5462
|
{
|
|
5458
5463
|
"path": "modules/lifecycle/routes/goals.js",
|
|
5459
5464
|
"mode": "0000644",
|
|
5460
|
-
"sha256": "
|
|
5465
|
+
"sha256": "a92a6024969ed0d76902770995a6787933bc87618b9601f057907ba7b07ad8a6"
|
|
5461
5466
|
},
|
|
5462
5467
|
{
|
|
5463
5468
|
"path": "modules/lifecycle/routes/help-requests.js",
|
|
@@ -5492,7 +5497,7 @@
|
|
|
5492
5497
|
{
|
|
5493
5498
|
"path": "modules/lifecycle/routes/task-write-routes.js",
|
|
5494
5499
|
"mode": "0000644",
|
|
5495
|
-
"sha256": "
|
|
5500
|
+
"sha256": "0a086a473b3e0267b4ad250a98a6551c952370da5bfdf5232738e04b0eaf59d6"
|
|
5496
5501
|
},
|
|
5497
5502
|
{
|
|
5498
5503
|
"path": "modules/lifecycle/routes/tasks.js",
|
|
@@ -7212,12 +7217,12 @@
|
|
|
7212
7217
|
{
|
|
7213
7218
|
"path": "package-lock.json",
|
|
7214
7219
|
"mode": "0000644",
|
|
7215
|
-
"sha256": "
|
|
7220
|
+
"sha256": "79dfc35495b354b8e53ac84d6210c5c84e8d1bd6a97fc08c84dcbcfd7cc7379d"
|
|
7216
7221
|
},
|
|
7217
7222
|
{
|
|
7218
7223
|
"path": "package.json",
|
|
7219
7224
|
"mode": "0000644",
|
|
7220
|
-
"sha256": "
|
|
7225
|
+
"sha256": "5c08ffd017c27b4ac3a97a55988bbd1fd9958a1ac15ab9e4a689af04531889b6"
|
|
7221
7226
|
},
|
|
7222
7227
|
{
|
|
7223
7228
|
"path": "public-docs/index.html",
|
|
@@ -8887,7 +8892,7 @@
|
|
|
8887
8892
|
{
|
|
8888
8893
|
"path": "src/module-api.js",
|
|
8889
8894
|
"mode": "0000644",
|
|
8890
|
-
"sha256": "
|
|
8895
|
+
"sha256": "980689b2fd9c8e0db4213b6ef449b68cef246f40ff3752925562f0f7ed818a16"
|
|
8891
8896
|
},
|
|
8892
8897
|
{
|
|
8893
8898
|
"path": "src/module-loader/catalog.js",
|
|
@@ -9927,7 +9932,7 @@
|
|
|
9927
9932
|
{
|
|
9928
9933
|
"path": "tests/goal_advisory.mjs",
|
|
9929
9934
|
"mode": "0000644",
|
|
9930
|
-
"sha256": "
|
|
9935
|
+
"sha256": "b8c179ae6c5ad962356e5d82a5c6aa89bd523913de10799e53de16279404d07d"
|
|
9931
9936
|
},
|
|
9932
9937
|
{
|
|
9933
9938
|
"path": "tests/goal_category.mjs",
|
|
@@ -9952,7 +9957,7 @@
|
|
|
9952
9957
|
{
|
|
9953
9958
|
"path": "tests/goal_criterion_confirm_ui.mjs",
|
|
9954
9959
|
"mode": "0000644",
|
|
9955
|
-
"sha256": "
|
|
9960
|
+
"sha256": "6bb3b8a0257c75422a6588e64fb8afee03cbf9c2e87edb90f9c9aaf2be89687c"
|
|
9956
9961
|
},
|
|
9957
9962
|
{
|
|
9958
9963
|
"path": "tests/goal_dependencies_read.mjs",
|
|
@@ -9977,7 +9982,7 @@
|
|
|
9977
9982
|
{
|
|
9978
9983
|
"path": "tests/goal_routes.mjs",
|
|
9979
9984
|
"mode": "0000644",
|
|
9980
|
-
"sha256": "
|
|
9985
|
+
"sha256": "0cc660cffe989b0d8efe5e2e73731d34e7e761ed4d27349809cd1dc9f26fcd75"
|
|
9981
9986
|
},
|
|
9982
9987
|
{
|
|
9983
9988
|
"path": "tests/goal_scope_check.mjs",
|
|
@@ -49,7 +49,7 @@ export interface GetGithubRepoResponse { ok: boolean; repo: unknown; private_dep
|
|
|
49
49
|
export interface GetGithubReposResponse { repos: unknown }
|
|
50
50
|
export interface GetGoalsIdConflictsResponse { ok: boolean; goal_id: unknown; conflicts: unknown; cross_goals_considered: unknown }
|
|
51
51
|
export interface GetGoalsIdRequestsResponse { requests: unknown }
|
|
52
|
-
export interface GetGoalsIdResponse { goal: unknown; members: unknown; criteria: unknown; dependencies: unknown }
|
|
52
|
+
export interface GetGoalsIdResponse { goal: unknown; members: unknown; criteria: unknown; dependencies: unknown; criterionless_task_count: unknown }
|
|
53
53
|
export interface GetGoalsResponse { goals: unknown }
|
|
54
54
|
export interface GetGoalsRollupsResponse { rollups: unknown }
|
|
55
55
|
export interface GetGovernmentAssignmentsResponse { assignments: unknown }
|
|
@@ -155,13 +155,15 @@ as `#ideas` has always given that person, which is the actual "nothing lands unv
|
|
|
155
155
|
survived.
|
|
156
156
|
|
|
157
157
|
The companion interview decision — **"Discord `#ideas` files with HINTS only (`suggested_goal_id`),
|
|
158
|
-
never routes"** — is
|
|
159
|
-
|
|
160
|
-
`modules/discord/discord-inbound.js`'s `#ideas` capture
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
never routes"** — is what R12 (task 1003068, "Discord `#ideas` carries routing hints") shipped, correcting
|
|
159
|
+
this ADR's earlier divergence note (which read `status='ready'` at the time it was written).
|
|
160
|
+
`modules/discord/discord-inbound.js`'s `#ideas` capture now sets `suggested_goal_id` (a classifier guess
|
|
161
|
+
via `suggestGoalForCapture`) and `suggested_version` (defaulting to the most-recently-started open
|
|
162
|
+
version even when no goal matches) on the filed idea row — both READ-ONLY hints for a human triaging via
|
|
163
|
+
`/idea-triage` (R13) — and never sets `goalId`, the only field that would make `captureIdea` actually
|
|
164
|
+
route. Every `#ideas` filing still lands in `idea_inbox` exactly as the pre-routing path did, now
|
|
165
|
+
carrying a hint rather than none, consistent with every other "classifier guess, not a route" vector
|
|
166
|
+
this ADR documents — the second bypass idea 1000771 named did not reopen.
|
|
165
167
|
|
|
166
168
|
## Consequences
|
|
167
169
|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# 0235 — A fix category is `kind='bug'` under a goal, not a new column
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-09-03
|
|
5
|
+
- **Tasks:** [#1003215](https://cloudbongos.com/builders#/task/1003215) (this decision — the structural half
|
|
6
|
+
of idea 1000828), [#1003214](https://cloudbongos.com/builders#/task/1003214) (the advisory's first quiet
|
|
7
|
+
case), [#1003157](https://cloudbongos.com/builders#/task/1003157) (extended the same exemption to the
|
|
8
|
+
criterion-link advisory and the goal detail view)
|
|
9
|
+
- **Goal:** [#1000071](https://cloudbongos.com/builders#/goal/1000071) — Idea routing
|
|
10
|
+
- **Related:** [ADR 0086](<redacted>.md) (the real work hierarchy this decision keeps
|
|
11
|
+
intact), [ADR 0183](<redacted>.md) (why a container that never finishes is dangerous
|
|
12
|
+
now), [ADR 0192 "a category orients and authorises nothing"](<redacted>.md)
|
|
13
|
+
(the precedent this decision is *not* — see §2)
|
|
14
|
+
|
|
15
|
+
## 1. The problem
|
|
16
|
+
|
|
17
|
+
A fix for something that should already work needs a home. Filing it with no
|
|
18
|
+
`goal_id` lands it in the version's catch-all — no scope wall, no owner reviewing
|
|
19
|
+
it (the `no_goal`/`catch_all` gap `goal-advisory.js` already flags for everything
|
|
20
|
+
else). Filing it under a real goal used to trip a second, unrelated nudge: every
|
|
21
|
+
goal-bound task with no done-when criterion link reads as unattributed work
|
|
22
|
+
(task 1003157). Neither nudge is wrong for a *feature* — but a bug fix is not
|
|
23
|
+
delivering toward one of a goal's stated outcomes; it is restoring one. Idea
|
|
24
|
+
1000828 named this a missing **first-class grouping**: "a fix for something that
|
|
25
|
+
should already work," living *under* an existing goal, without a new goal being
|
|
26
|
+
born for it.
|
|
27
|
+
|
|
28
|
+
## 2. Why this is not a stored category, and not [ADR 0192](<redacted>.md)
|
|
29
|
+
|
|
30
|
+
The obvious-looking answer — add a `category` (or `bucket`) column to `tasks` —
|
|
31
|
+
is the wrong shape for two reasons specific to *this* system's schema, not taste:
|
|
32
|
+
|
|
33
|
+
1. **`tasks` stores no category at all, by decision.** [ADR 0192 "a category
|
|
34
|
+
orients and authorises nothing"](<redacted>.md)
|
|
35
|
+
already answered a structurally identical question for *goals* — a human label
|
|
36
|
+
from an owner-named set, advisory only, gating nothing — and its own text is
|
|
37
|
+
explicit that "a task stores no category… it inherits its goal's through
|
|
38
|
+
`tasks.goal_id`; a stored copy would be a second answer to one question." A
|
|
39
|
+
`tasks.fix_category` boolean would be exactly that second answer: `kind='bug'`
|
|
40
|
+
already exists, is already required (`migrations/006_pms_v2.sql`,
|
|
41
|
+
`NOT NULL DEFAULT 'unclassified'`), and already means "this is a fix." Adding
|
|
42
|
+
a column to restate it is duplication, not a new fact.
|
|
43
|
+
2. **This decision is a *narrower*, more binding one than [ADR 0192](<redacted>.md)'s.**
|
|
44
|
+
Its category is purely a label a human reads — nothing may branch on it.
|
|
45
|
+
"A bug fix needs no criterion link" and "a bug fix in the catch-all is quiet"
|
|
46
|
+
are the OPPOSITE: two decision surfaces (`goal-advisory.js`) DO branch on
|
|
47
|
+
`kind='bug'`, on purpose, because the alternative — nagging every bug fix to
|
|
48
|
+
either name a criterion it doesn't serve or justify a goal it doesn't need —
|
|
49
|
+
is the noise idea 1000828 asked to remove. So this ADR does not extend ADR
|
|
50
|
+
0192's "orients, never authorises" rule to `kind`; `kind` already carries real
|
|
51
|
+
weight elsewhere in this codebase (`task-classifier.js`'s heuristic
|
|
52
|
+
classifier decides it from title/description, and `<redacted>`
|
|
53
|
+
calibrates a task's estimate by it), and this decision adds one more narrow,
|
|
54
|
+
named case to that existing list rather than inventing a second category axis
|
|
55
|
+
beside it.
|
|
56
|
+
|
|
57
|
+
## 3. Why not a global "Bugs" goal either
|
|
58
|
+
|
|
59
|
+
Idea 1000828's own reasoning is the load-bearing part, and it survives unchanged
|
|
60
|
+
here: a goal carries `scope_modules`, the [ADR 0086](<redacted>.md)
|
|
61
|
+
§3 scope wall — a bugs goal spanning every module makes that wall either
|
|
62
|
+
everything or nothing, which is not a wall. And a goal carries done-when criteria
|
|
63
|
+
and can be marked `achieved`; a bug container never finishes, so making it a goal
|
|
64
|
+
manufactures a goal that can never legitimately reach `achieved` — polluting the
|
|
65
|
+
exact signal [ADR 0183](<redacted>.md)'s auto-satisfy now
|
|
66
|
+
depends on to cascade a goal to `achieved` the moment every linked task ships. A
|
|
67
|
+
category *under* an existing goal keeps that goal's wall, keeps the "things that
|
|
68
|
+
should work in THIS area" context, and never claims to be finishable on its own.
|
|
69
|
+
|
|
70
|
+
## 4. Decision
|
|
71
|
+
|
|
72
|
+
**A fix category is `kind='bug'` on a task that carries a real `goal_id` — no
|
|
73
|
+
schema change, no new column, no new goal type.** The goal is *finishable* as
|
|
74
|
+
before; the fix category is a lens over its existing `kind` field, exactly the
|
|
75
|
+
way [ADR 0192](<redacted>.md)'s goal category
|
|
76
|
+
is a lens over `goals.category_id`, just enforced more strictly because two real
|
|
77
|
+
gates read it.
|
|
78
|
+
|
|
79
|
+
**As-built** (task 1003214 first, task 1003157 completing it — both already
|
|
80
|
+
shipped on `main` before this ADR was written):
|
|
81
|
+
|
|
82
|
+
- `adviseGoal()` in [`modules/lifecycle/goal-advisory.js`](../../modules/lifecycle/goal-advisory.js)
|
|
83
|
+
is the single place all three exemptions live, in one pure, DB-free function:
|
|
84
|
+
a `kind='bug'` task is silent for `no_goal` (no goal at all — a fix needs no
|
|
85
|
+
home to be filed correctly), `catch_all` (a fix sitting in the version's
|
|
86
|
+
catch-all bucket is already where it belongs), and `no_criterion_link` (a real
|
|
87
|
+
goal's criteria named, but a bug fix delivers no criterion and needs none). The
|
|
88
|
+
function's own doc comment states the rule this ADR records: *"a fix is the
|
|
89
|
+
goal's 'fix category', not a delivery against one of its stated outcomes."*
|
|
90
|
+
- `countTasksWithoutCriterionLink()` in
|
|
91
|
+
[`modules/lifecycle/db-deps-criteria.js`](../../modules/lifecycle/db-deps-criteria.js),
|
|
92
|
+
surfaced on `GET /goals/:id` and rendered on the hall's goal detail view
|
|
93
|
+
(`modules/hall-ui/public/goals-render.js`), excludes `kind='bug'` from its
|
|
94
|
+
count for the identical reason — an intentionally unlinked fix must not read as
|
|
95
|
+
a standing defect on the goal's own page.
|
|
96
|
+
- **No hall/status change was needed for grouping.** Verified against the live
|
|
97
|
+
code, not assumed: every task card in a goal's To-do list already renders its
|
|
98
|
+
`kind` as a visible tag (`goals-render.js`, `task__tag`), and the goal detail
|
|
99
|
+
view's Filters disclosure already offers a `Kind` lens with `bug` as a value
|
|
100
|
+
(`goals-page.js`, `f-goal-kind`) — so "the hall groups bugs by kind" was
|
|
101
|
+
already true before this task, via the *same* general-purpose kind facility
|
|
102
|
+
every other kind uses. Building a bug-specific grouping widget beside it would
|
|
103
|
+
be the second-hierarchy mistake [ADR 0192](<redacted>.md)
|
|
104
|
+
§2 already named once for goal categories.
|
|
105
|
+
|
|
106
|
+
## 5. What was deliberately not built
|
|
107
|
+
|
|
108
|
+
- **No `tasks.category` / `tasks.fix_category` column, no enum, no CHECK
|
|
109
|
+
constraint.** `kind` already answers the question.
|
|
110
|
+
- **No global "Bugs" goal**, and no goal-creation shortcut that manufactures one
|
|
111
|
+
— §3 above is why that door stays shut.
|
|
112
|
+
- **No bug-specific grouping UI.** The kind tag + Kind filter already exist and
|
|
113
|
+
already work for `bug` the same as every other kind; a second, bug-only lens
|
|
114
|
+
beside a general one would duplicate it for no reader benefit.
|
|
115
|
+
|
|
116
|
+
## 6. Consequences
|
|
117
|
+
|
|
118
|
+
- Idea 1000820 and idea 1000788 (the `node_modules/pino` worktree-resolution
|
|
119
|
+
bugs cited as this decision's first real customers) now have somewhere to file
|
|
120
|
+
that advances a goal without inventing one — the mechanism task 1003215 asked
|
|
121
|
+
for was already load-bearing on `main` by the time this ADR was written; this
|
|
122
|
+
document is what makes the decision citable rather than three commit messages.
|
|
123
|
+
- The exemption is enforced in exactly one function (`adviseGoal`) and one query
|
|
124
|
+
(`countTasksWithoutCriterionLink`); a third surface that starts branching on
|
|
125
|
+
`kind='bug'` for a similar reason should extend those, not invent a fourth
|
|
126
|
+
definition of "what counts as a fix."
|
|
127
|
+
- If a future need ever wants `kind='bug'` to carry MORE weight than an advisory
|
|
128
|
+
exemption — a claim-time gate, a reward multiplier, a dependency rule — that is
|
|
129
|
+
a new, explicit decision needing its own ADR, the same posture
|
|
130
|
+
[ADR 0192](<redacted>.md) §5 holds for goal
|
|
131
|
+
categories. This ADR does not pre-authorise that.
|
package/docs/adr/README.md
CHANGED
|
@@ -322,5 +322,6 @@ This keeps the decision history honest and traceable.
|
|
|
322
322
|
|
|
323
323
|
| 0233 | [**A copy proposal is a task carrying a patch, and the applier is the only writer** ([task 1003114](https://cloudbongos.com/builders#/task/1003114) · goal 1000074, criterion `artist-edits-land-as-tasks`). Goal 1000074 asks for two things that pull against each other in its own text: the artist must change copy WITHOUT an engineer, and there must be NO LIVE CMS because every copy change is a claimed, graded, shipped task. R02 shipped the half that needs no reconciliation (the flag) and [ADR 0178](<redacted>.md) §3 pinned the absence of any column that could hold replacement text, naming this task as the one that would have to keep it true. Decisions: (1) the shape is **borrowed from the design half, not invented** — ADR 0081 already has a tool authoring and an `adapter.export()` landing the output as a committed diff under a claim inside a path guard, so the artist is the tool, the copy desk is where they author, and the proposal is the export. (2) A proposal is a **TASK carrying a fenced `copy-proposal` patch**, never a row: the ledger is where a proposal belongs anyway, nothing renders a task description as product copy, a reviewer can read the change without running anything, and `copy-desk` gains NO migration (the obvious-but-wrong R03 is a `copy_desk_proposals` table, which is the forbidden column with extra steps). The module gains `consumes: ["lifecycle"]` and calls `createTask` through the port — it can put a request INTO the ledger and nothing can pull copy OUT of it. (3) `scripts/gds/copy-apply.js` **re-derives the position** rather than trusting the patch, which required teaching R01s extractor to record a source SPAN and deliberately dropping it from the committed registry: an offset goes stale the moment anything above it changes, while the id is designed to survive exactly that movement, so the span is recomputed at the moment of the edit by the same code that decided the string was copy. (4) The **placeholder arithmetic** is what makes a patch mechanical rather than a note for an engineer — the applier puts the sources own `${expr}` back, k-th hole to k-th expression, so an artist may MOVE a value within a sentence but never add or drop one, checked at proposal time and again against the real source. (5) Every ambiguity is a **named refusal** (`target_gone`, `ambiguous_target`, `no_exact_span`, `placeholder_mismatch`, `path_outside_surface` — the design adapters path guard, derived from R01s own SURFACES), because a wrong edit is worse than no edit; after writing, the applier regenerates the registry and checks that what it wrote is what the inventory now reads. (6) Proposing is **open to every rank** (an artist is a craft, not a rank) and lands at `backlog`, so a proposal is a request that passes ordinary triage. Rejected: a proposals table, a column beside the flag, trusting the patchs line number, an escape-aware index map to buy spans for nine strings, auto-closing the flag a proposal answers.](<redacted>.md) | copy-desk / the artist loop |
|
|
324
324
|
| 0234 | [**Idea routing: capture-time promotion, the landing matrix, and the homeless-only inbox** ([task 1003064](https://cloudbongos.com/builders#/task/1003064) · goal 1000071, "Idea routing"). Idea 1000771 named the failure: Discord `#bugs` created tasks DIRECTLY against a hardcoded, already-shipped version — no `captured_by`, no lineage, so the ideator-credit lane had nothing to pay against — while every other open idea sat waiting for a human triage pass that dropped its promotion into a version catch-all, because nothing in capture ever named a goal. Records what R01/R02/R05/R07/R13 shipped. `captureIdea` (`modules/ideas/inbox.js`) stays the ONE capture chokepoint — routing is one optional `goalId` argument, never a second creation door — and when the matrix routes, `routeIdeaToGoal` writes the idea row, the task (via the `lifecycle.createTask` port) and an `ideas_task_lineage` row (`origin='route'`) in ONE transaction, so a route can never mint attribution-free work the way `#bugs` did. `modules/ideas/routing.js`'s `decideLanding` is PURE and fail-closed: a bug from a Metic+ filer hard-lands `ready`, a sub-Metic bug or any other kind with a goal soft-lands `backlog`, a Full Idea or `criterion-proposal`/`decision` never routes, no goal named stays the pre-routing inbox path. `authorizeRoutedLanding` is the BINDING gate underneath the matrix's decision — a goal reaching protected scope raises the task's rank floor to Metic and REFUSES a sub-Metic filer outright, while membership is deliberately NOT required (reporting isn't authoring). `core_222`'s `suggested_goal_id` is an advisory hint only — the real home is `tasks.goal_id`, set by construction — and `/idea-triage` (`scripts/gds/triage.js`) now sends `goal_id` on promotion, so the inbox is homeless-only. Divergence recorded: R11 (Discord `#bugs`) hard-lands a linked Metic+ builder's bug immediately through the SAME shared matrix rather than the spec's "always soft-land, pending queue" design, because a local copy of the rank split would only drift from the one definition; and R12 (Discord `#ideas` hints) is NOT YET SHIPPED, so `#ideas` carries no goal hint today despite the interview decision that it should, hint-only, never route.](<redacted>.md) | ideas / lifecycle |
|
|
325
|
+
| 0235 | [**A fix category is `kind='bug'` under a goal, not a new column** ([task 1003215](https://cloudbongos.com/builders#/task/1003215) · goal 1000071, "Idea routing" — the structural half of idea 1000828; task 1003214 shipped the advisory's first quiet case, task 1003157 extended it). A fix for something that should already work needs a home under an existing goal WITHOUT a new goal being born and WITHOUT a stored column restating what `tasks.kind` already says. Not a global "Bugs" goal either: a goal spanning every module makes the [ADR 0086](<redacted>.md) scope wall either everything or nothing, and a goal that can never reach `achieved` (a bug container never finishes) pollutes the exact signal [ADR 0183](<redacted>.md)'s auto-satisfy depends on. Not [ADR 0192 "a category orients and authorises nothing"](<redacted>.md) either, despite the structural resemblance: that decision forbids any gate from branching on a goal's category, while THIS decision is two decision surfaces deliberately branching on `kind='bug'` — `adviseGoal()` (`modules/lifecycle/goal-advisory.js`) goes silent for a bug under all three of its reasons (`no_goal`, `catch_all`, `no_criterion_link` — "a fix is the goal's 'fix category', not a delivery against one of its stated outcomes," its own doc comment) and `countTasksWithoutCriterionLink()` (`modules/lifecycle/db-deps-criteria.js`, surfaced on the hall's goal detail view) excludes `kind='bug'` from its count for the same reason. No hall/status change was needed for GROUPING bugs under a goal: every task card already renders its `kind` as a visible tag and the goal detail view's Filters already offer a `Kind` lens with `bug` as a value — verified against the live code, not built new. Rejected: a `tasks.category`/`fix_category` column or enum, a global cross-module "Bugs" goal, a bug-specific grouping widget beside the general-purpose kind facility that already works.](<redacted>.md) | ideas / lifecycle |
|
|
325
326
|
|
|
326
327
|
> ⚠️ **Numbering collisions are now machine-enforced, not narrated.** Twenty numbers are shared by two ADRs each — assigned in parallel sessions before anything checked. The files keep their filenames (renumbering would break every existing citation, and a number once assigned is never reused), and this table disambiguates each pair as `NNNN-a` / `NNNN-b`. The authoritative list is `LEGACY_DUPLICATE_ADRS` in [`scripts/gds/adr-namespace.js`](../../scripts/gds/adr-namespace.js), frozen by exact filename: a **new** collision — or a third file joining a legacy number, or a rename of either half — hard-fails the `unit` CI gate, as does an index row that points at no file or an ADR with no row. The list may only shrink. This paragraph used to narrate the collisions one by one and had fallen eight behind reality; see [ADR 0195](<redacted>.md) for why the check exists and why the pairs are grandfathered rather than renumbered.
|
package/docs/api/openapi.json
CHANGED
|
@@ -16332,13 +16332,15 @@
|
|
|
16332
16332
|
"goal": {},
|
|
16333
16333
|
"members": {},
|
|
16334
16334
|
"criteria": {},
|
|
16335
|
-
"dependencies": {}
|
|
16335
|
+
"dependencies": {},
|
|
16336
|
+
"criterionless_task_count": {}
|
|
16336
16337
|
},
|
|
16337
16338
|
"required": [
|
|
16338
16339
|
"goal",
|
|
16339
16340
|
"members",
|
|
16340
16341
|
"criteria",
|
|
16341
|
-
"dependencies"
|
|
16342
|
+
"dependencies",
|
|
16343
|
+
"criterionless_task_count"
|
|
16342
16344
|
]
|
|
16343
16345
|
},
|
|
16344
16346
|
"GetGoalsResponse": {
|