@bongos/core 1.19.363 → 1.19.365

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.363",
6
- "core_contract": "1.19.363",
7
- "source_commit": "dfe3198cbb7ad4104276759b209d6064e4075753",
5
+ "core_version": "1.19.365",
6
+ "core_contract": "1.19.365",
7
+ "source_commit": "cf24b657bacb6c291ab9351c73cfd93be421c62f",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-25T19:24:12.550Z",
9
+ "built_at": "2026-08-25T20:07:12.580Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 330,
12
+ "docs_redacted": 331,
13
13
  "agent_docs_stubbed": 22,
14
- "functional_verbatim": 1527,
14
+ "functional_verbatim": 1529,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1879,
20
- "tree_sha256": "11a13769c884c5b71eefc6c2d9d62e17d1096f782583e2b0cdd46ae4a92cf52d",
19
+ "file_count": 1882,
20
+ "tree_sha256": "7ecb76bb4cadcc01fbe7adb2a6301d5dd509e1d98ab5ea776486fb7499fc877b",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -1444,10 +1444,15 @@
1444
1444
  "mode": "0000644",
1445
1445
  "sha256": "bbf7e4f1903e519952dc80515b9c590a9f62a51160394d9d6cb07b378f7748e5"
1446
1446
  },
1447
+ {
1448
+ "path": "docs/adr/0191-majority-and-the-author-who-may-not-decide-alone.md",
1449
+ "mode": "0000644",
1450
+ "sha256": "0ae7f6a69d9006c54210cea4f6a846445a6181f148bcb4de979011b46cca5188"
1451
+ },
1447
1452
  {
1448
1453
  "path": "docs/adr/README.md",
1449
1454
  "mode": "0000644",
1450
- "sha256": "1794e5de943ef65de6c9c0db0e82732b704c5261fe8bd8bef460ef5cc760c749"
1455
+ "sha256": "cba01a64b2f86f9e10e6a7306bf23d3fbebe16b2e5d7ddd04f68a1113a3ae525"
1451
1456
  },
1452
1457
  {
1453
1458
  "path": "docs/api-reference.md",
@@ -1482,12 +1487,12 @@
1482
1487
  {
1483
1488
  "path": "docs/copy-inventory.md",
1484
1489
  "mode": "0000644",
1485
- "sha256": "38406391787351c72cdda4d886314b93986c22e24e4396546595913463adf22c"
1490
+ "sha256": "e94098fe2d2dd9d82bc6f8f4c3db32be22cb42f748ca58855670345fe9335176"
1486
1491
  },
1487
1492
  {
1488
1493
  "path": "docs/copy-registry.json",
1489
1494
  "mode": "0000644",
1490
- "sha256": "3541058ca6221c024e2129d1e8764aff8094671e291ab992c1767fc10ff52e35"
1495
+ "sha256": "13aa52dd415a69e1cef30b0d972f507cd397e0c7a3a3843c84eeab35de75cc6f"
1491
1496
  },
1492
1497
  {
1493
1498
  "path": "docs/design-contract.md",
@@ -2552,7 +2557,7 @@
2552
2557
  {
2553
2558
  "path": "modules/builder-settings/builder-needs.js",
2554
2559
  "mode": "0000644",
2555
- "sha256": "276a92a2000f2bc0e9e157791947f2ee7e6bfe934a15a996d42dba13984c26d7"
2560
+ "sha256": "0668dd0b60184bb230e60f13ee38dffb3a7831264e5155c3bafab3a2dce0f7f5"
2556
2561
  },
2557
2562
  {
2558
2563
  "path": "modules/builder-settings/db.js",
@@ -2937,7 +2942,7 @@
2937
2942
  {
2938
2943
  "path": "modules/discord/board-broadcast.js",
2939
2944
  "mode": "0000644",
2940
- "sha256": "111b577698792565f55e66f031c6567ac548b4fc5194b51c262843b2bec66198"
2945
+ "sha256": "36906948331be98d0e14b7d0a02d0c86533b2d6247a3506e88c5c52827615411"
2941
2946
  },
2942
2947
  {
2943
2948
  "path": "modules/discord/bug-attachments.js",
@@ -3027,7 +3032,7 @@
3027
3032
  {
3028
3033
  "path": "modules/discord/webhook.js",
3029
3034
  "mode": "0000644",
3030
- "sha256": "59d2452516f4568c2a99c599275224ff0d2ff86b763066469a567944efbf5f69"
3035
+ "sha256": "f8dc8f411b243d19d28b0b2f25fdc08925554e2d1b329ed2571aa7461b8cb64a"
3031
3036
  },
3032
3037
  {
3033
3038
  "path": "modules/economy/CLAUDE.md",
@@ -3112,7 +3117,7 @@
3112
3117
  {
3113
3118
  "path": "modules/government/board.js",
3114
3119
  "mode": "0000644",
3115
- "sha256": "e6e01e38328eb6d234c76d7f5aa9e96589fd2c9ef89c105216495303ac1e3fdd"
3120
+ "sha256": "c3fb035f37f2a8fa8f0c8f3e06ecef037a2d46937e6f3958cdd259573b426d9c"
3116
3121
  },
3117
3122
  {
3118
3123
  "path": "modules/government/catalog.js",
@@ -3122,12 +3127,12 @@
3122
3127
  {
3123
3128
  "path": "modules/government/config.js",
3124
3129
  "mode": "0000644",
3125
- "sha256": "62fc47cb6775b1af86bf7f22773180b390d584230b87af6bb1ec20a841be57e1"
3130
+ "sha256": "a6ed17999e34222a7c8622699c33c5302afbf2fff9024fefb20177d029dbf8a5"
3126
3131
  },
3127
3132
  {
3128
3133
  "path": "modules/government/db.js",
3129
3134
  "mode": "0000644",
3130
- "sha256": "e48b8e01738969ca589496ee0bea94b5a31995e853a1347f63ca83895d4d28fd"
3135
+ "sha256": "99be259855d63808adca140222eaf8563cff054b06ecc1acdd49594fcfc129fe"
3131
3136
  },
3132
3137
  {
3133
3138
  "path": "modules/government/membership-predicate.js",
@@ -3242,7 +3247,7 @@
3242
3247
  {
3243
3248
  "path": "modules/government/routes/government.js",
3244
3249
  "mode": "0000644",
3245
- "sha256": "8b29ea6478f4854dd5c74ea3538079db818707a575318634ce4f7504d2768289"
3250
+ "sha256": "6c38766b7b2a76a5fc035ad4eefce41fe45739730e70fb26b40546bded7b66b9"
3246
3251
  },
3247
3252
  {
3248
3253
  "path": "modules/government/routes/rank-sync.js",
@@ -3567,7 +3572,7 @@
3567
3572
  {
3568
3573
  "path": "modules/hall-ui/public/government.js",
3569
3574
  "mode": "0000644",
3570
- "sha256": "e84ece3c9243bdf1423ad3d62b388e5c438e662b55378154d71701e5084dee75"
3575
+ "sha256": "fb7cf8ab06540304e2ee8197cdc7c0f36becb71dca7fbb32c7c12d8a6a52efcf"
3571
3576
  },
3572
3577
  {
3573
3578
  "path": "modules/hall-ui/public/hall-kit.js",
@@ -4852,12 +4857,12 @@
4852
4857
  {
4853
4858
  "path": "package-lock.json",
4854
4859
  "mode": "0000644",
4855
- "sha256": "44644726a2b97729a15e7220d9bc5aa7b7a394580ae95da58b452e8824688ff6"
4860
+ "sha256": "0c5e3a6968b8cea0f192e4b7afabd3d56ebdfb4c195caa6b2e2c83488f747994"
4856
4861
  },
4857
4862
  {
4858
4863
  "path": "package.json",
4859
4864
  "mode": "0000644",
4860
- "sha256": "2dc2136427cf6df206810b410527d3cc09cb3ac72c663f49d0c0ad3bb8a3951c"
4865
+ "sha256": "690bded1bc02daf39aab34106ac5430080ed1125978e8e4d641b60c9fd465dd9"
4861
4866
  },
4862
4867
  {
4863
4868
  "path": "public-docs/index.html",
@@ -6652,7 +6657,7 @@
6652
6657
  {
6653
6658
  "path": "src/bongos/routes/me.js",
6654
6659
  "mode": "0000644",
6655
- "sha256": "0006bf8ec2430532a72c194bcc7517b4008d00b8fa4a73e9a77bc2c6a10c6d26"
6660
+ "sha256": "34e50788c8162430b46e9f204c36396b456d0524bde3063434bb848d7977cf07"
6656
6661
  },
6657
6662
  {
6658
6663
  "path": "src/bongos/routes/modules.js",
@@ -6722,7 +6727,7 @@
6722
6727
  {
6723
6728
  "path": "src/module-api.js",
6724
6729
  "mode": "0000644",
6725
- "sha256": "df48c1828cda7dc37f655dcb890871fd2fd5867f223823eff07a8ae74dd1febe"
6730
+ "sha256": "29fe80fe994a985cfbc64db7d7835b09485ed7dceae578fab86d0fcb99eccc46"
6726
6731
  },
6727
6732
  {
6728
6733
  "path": "src/module-loader/catalog.js",
@@ -7467,7 +7472,7 @@
7467
7472
  {
7468
7473
  "path": "tests/discord_board_broadcast.mjs",
7469
7474
  "mode": "0000644",
7470
- "sha256": "db1a02168869e633fccc62137f4db3e0f4efbf4a785db7cc0b49a30a8d4f418a"
7475
+ "sha256": "f3ac413d7653d3027516eae052523b98206cb54452cb3410602e1bb85c4d4c19"
7471
7476
  },
7472
7477
  {
7473
7478
  "path": "tests/discord_bot.mjs",
@@ -7767,18 +7772,23 @@
7767
7772
  {
7768
7773
  "path": "tests/government_board_amendment.mjs",
7769
7774
  "mode": "0000644",
7770
- "sha256": "54b0d380414a077b072080760a24cfc813d22249f8786b96003ab2e010081b4d"
7775
+ "sha256": "eec3a845e513cc8cf1faf6cde195581943c52b249508091db7d141209add4874"
7771
7776
  },
7772
7777
  {
7773
7778
  "path": "tests/government_board_close.mjs",
7774
7779
  "mode": "0000644",
7775
- "sha256": "4408ec434f36b70a589b0592814733d3f88d48aa9aff38c08c9a2eb6c6c9f7b3"
7780
+ "sha256": "86c129ee24f084fcd5574a827de0ed4ada7a00a92721b1038204f5474ee2e18d"
7776
7781
  },
7777
7782
  {
7778
7783
  "path": "tests/government_board_expiry.mjs",
7779
7784
  "mode": "0000644",
7780
7785
  "sha256": "1bf22bc4ae339459e28a9f4e7975aac9e9d84eacd3f901d30371b44574f714c1"
7781
7786
  },
7787
+ {
7788
+ "path": "tests/government_board_majority.mjs",
7789
+ "mode": "0000644",
7790
+ "sha256": "c166f0ab3e26921944717d0dfc78eb186e68b0666efded779e5ad6dcd1f23522"
7791
+ },
7782
7792
  {
7783
7793
  "path": "tests/government_board_membership.mjs",
7784
7794
  "mode": "0000644",
@@ -7789,6 +7799,11 @@
7789
7799
  "mode": "0000644",
7790
7800
  "sha256": "7da08f5ad67ca1d42d0ef2afb641d1a5f1d5e0c6e0267e8b5f3ad0d275603a36"
7791
7801
  },
7802
+ {
7803
+ "path": "tests/government_board_summons.mjs",
7804
+ "mode": "0000644",
7805
+ "sha256": "349bf135c0c38fb23d40142e3aaa2a5aada9484a4cead9c5b312450a9f818c07"
7806
+ },
7792
7807
  {
7793
7808
  "path": "tests/government_board_view.mjs",
7794
7809
  "mode": "0000644",
@@ -7797,12 +7812,12 @@
7797
7812
  {
7798
7813
  "path": "tests/government_board_vote.mjs",
7799
7814
  "mode": "0000644",
7800
- "sha256": "ed31b8c643d2b9279f4d7e70a1f2ab67d8fbe620c1f61ce8496ec858247d7d26"
7815
+ "sha256": "7fc989454776542675031a29a98aa21a1ce477e61aa0806ed6a602a55e87e186"
7801
7816
  },
7802
7817
  {
7803
7818
  "path": "tests/government_board_vote_ui.mjs",
7804
7819
  "mode": "0000644",
7805
- "sha256": "a9e46b10cf9b6c17550f22463d13caa0d4b63d87d4fc63d087dd14e4b95b8312"
7820
+ "sha256": "2159eef5902e14576c39cbc106deaf757cf687e62f5d5f893e3f06a239a277f5"
7806
7821
  },
7807
7822
  {
7808
7823
  "path": "tests/government_board_window.mjs",
@@ -7827,7 +7842,7 @@
7827
7842
  {
7828
7843
  "path": "tests/government_config.mjs",
7829
7844
  "mode": "0000644",
7830
- "sha256": "330372412c80d8d33698eba68a5c19e79daff17b0d820a9a797c883071a5641f"
7845
+ "sha256": "2e6f51648c858cf33cf6eb712a20e048be3fa92d2ec6d7e747601569b3f19109"
7831
7846
  },
7832
7847
  {
7833
7848
  "path": "tests/government_constitution_view.mjs",
@@ -0,0 +1,142 @@
1
+ # 0191 — Majority, and an author who may vote but never decide alone
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-08-25
5
+ - **Tasks:** [#1003273](https://cloudbongos.com/builders#/task/1003273). Diagnosis and companion fix: [#1003271](https://cloudbongos.com/builders#/task/1003271) (the summons), [#1003269](https://cloudbongos.com/builders#/task/1003269) (the Government tab order).
6
+ - **Supersedes, in part:** [ADR 0175](<redacted>.md) — §3's pass-rule set (`first_ratifier | consent`) and §§7/11's flat anti-self-vote rule. Everything else in 0175 stands, including §8 (Discord is outbound-only, never a ballot), which this decision explicitly re-affirms.
7
+
8
+ ## 1. The report, and why the obvious diagnosis was wrong
9
+
10
+ On 2026-08-25 the owner reported that "voting seems to be bugged … we have
11
+ multiple ideas that need full idea ratification in queue but the voting ability
12
+ does not seem to exist at present."
13
+
14
+ Nothing was broken. Nine items sat open, every one scoring 100 on completeness,
15
+ under `pass_rule: first_ratifier` and `membership: rank:archon`. Four archons were
16
+ active, so **any one of three non-authors could have ratified any item with a
17
+ single click.** Two independent facts made that invisible:
18
+
19
+ 1. **Every open item was authored by the one person looking at them.** ADR 0175's
20
+ anti-self rule refused an author's vote, and the hall honoured it by rendering
21
+ *no ballot at all* — so the Board Room showed nine sittings and no way to vote
22
+ on any of them. From the owner's seat the feature genuinely did not exist.
23
+ 2. **Nobody else had been told.** The Discord announcer was the only summons, and
24
+ it is best-effort by construction. That half is fixed separately in #1003271.
25
+
26
+ The lesson worth keeping: **a rule enforced by removing the control is
27
+ indistinguishable from a broken feature.** The anti-self rule was correct in
28
+ intent and invisible in operation.
29
+
30
+ ## 2. The pass rule becomes `majority`, and majority means more than half of the MEMBERSHIP
31
+
32
+ The owner's decision: *"voting needs majority or whatever other type of conditions
33
+ for passing that a project actually lets into place. So for this project, it should
34
+ be majority."*
35
+
36
+ `majority` did not exist. `PASS_RULES` was `['first_ratifier', 'consent']`, and
37
+ neither is a majority:
38
+
39
+ | Rule | What decides | **What silence means** |
40
+ |---|---|---|
41
+ | `first_ratifier` | the first vote cast | an unvoted item **passes** |
42
+ | `consent` | a reasoned objection returns; anything else passes | an empty window **passes unopposed** |
43
+ | `majority` (new) | more than half of the resolved membership votes yes | an item that never reaches a majority **returns** |
44
+
45
+ The three are **not a ladder of strictness** — they differ on what silence means,
46
+ which is the axis that actually matters. That framing is now the comment in
47
+ `config.js`, because "which is stricter" is the wrong question to ask of them.
48
+
49
+ Two sub-decisions, both deliberate:
50
+
51
+ - **Of the MEMBERSHIP, not of the votes cast.** Two yes votes out of nine members
52
+ is not a majority however unanimous the turnout was. Counting cast votes would
53
+ let low turnout quietly pass things, which is the failure mode the owner's word
54
+ "majority" is reaching for.
55
+ - **Strictly more than half, never `>=`.** On the live board of four that is three,
56
+ not two. A tie is not a majority.
57
+
58
+ `majority` also closes early when the outcome is **already fixed** — carried, or
59
+ arithmetically unreachable given that v1 has no vote-changing. That is not
60
+ impatience; it is the same reasoning that makes `consent` close on full turnout.
61
+
62
+ ## 3. An author may vote, and may never decide alone
63
+
64
+ Replacing ADR 0175 §§7/11's flat refusal. The owner's rule: *"authors can vote if
65
+ also a voting member"* — and an item *"needs at least one vote from a non-author to
66
+ pass."*
67
+
68
+ **The rule did not get weaker; it moved to where deciding actually happens.**
69
+ Refusing the cast was only ever a proxy for "an author must not decide their own
70
+ idea", and it was the proxy that produced the deadlock. Enforcement is now at the
71
+ two points where something is actually decided:
72
+
73
+ 1. **Closing it** — `shouldCloseAfterVote` requires a vote that is not the
74
+ author's, under *every* rule. Without this an author could still close their own
75
+ item under `first_ratifier`.
76
+ 2. **Carrying it** — the majority tally requires at least one **non-author yes**.
77
+ A majority made only of the author cannot ratify, on any board, including a
78
+ one-member one.
79
+
80
+ Three consequences we chose on purpose:
81
+
82
+ - **The author's yes still counts toward the numerator.** They sit on the board and
83
+ it is a real vote; it simply can never be the whole of a majority. Author + two
84
+ peers = three of four = carried.
85
+ - **An author cannot make their own idea worse.** Their `no` is excluded from the
86
+ objection test. Otherwise the safest act available to an author would be silence
87
+ — a strange incentive to build into a board.
88
+ - **A self-cast earns no karma.** The +1 rewards reading someone else's idea and
89
+ taking a position on it. Paying it for a self-vote would make an author's own
90
+ ballot a small income stream.
91
+
92
+ **The amendment carve-out survives, and is now the author rule's *scope* rather
93
+ than an exception to it.** ADR 0175 §7 blesses the monarchy act: on a one-Archon
94
+ board the proposer must be able to ratify their own amendment or the subject type
95
+ is dead on arrival. So the author rule applies to `full_idea` only — an amendment
96
+ pays nothing and has no author to wait for. In code that is one predicate
97
+ (`authorRuleApplies`), and for an amendment the deciding author is `null`, so the
98
+ trigger and tally behave exactly as they did before this change. A test asserts
99
+ both sides.
100
+
101
+ ## 4. The clock is a consequence of majority, not an independent fix
102
+
103
+ #1003271 was originally scoped to add a sitting clock, because nine items with
104
+ `window_minutes: null` could never lapse. **Implementing that would have destroyed
105
+ the very ideas it was meant to unstick**, and the reason is one line — `tallyOutcome`:
106
+
107
+ ```js
108
+ return (votes || []).some((v) => v.direction === 'no') ? 'returned' : 'passed';
109
+ ```
110
+
111
+ **Zero votes passes.** Give those sittings a deadline while `first_ratifier` is in
112
+ force and the expiry sweep closes all nine as *ratified, unread* — paying author
113
+ ratification karma and the stream-3 idea credit on ideas nobody voted on.
114
+
115
+ So a clock is only safe under a rule where expiry means **return**, which is what
116
+ `majority` provides. The two ship together, and `BOARD_DEFAULTS.window_minutes`
117
+ stays `null` in the neutral core: a project that wants a clock wants `majority`
118
+ first, and the neutral default must not hand every instance an auto-ratifier.
119
+
120
+ ## 5. What this costs, stated plainly
121
+
122
+ Under majority on a four-member board, **three affirmative votes are needed for
123
+ anything to pass.** That is a real bar, and on a quiet board the honest outcome of
124
+ an idea nobody engages with is now *returned* rather than silently ratified. That
125
+ is the point of the owner's choice — but it means a generous window and a working
126
+ summons (#1003271) are load-bearing, not cosmetic. A majority board with no
127
+ summons would return everything.
128
+
129
+ **A solo instance still cannot ratify its own ideas.** One member who is also the
130
+ author has no non-author yes available, so their ideas return. This is inherent in
131
+ "never decide alone" and is the correct default for the portable core — an
132
+ instance that wants otherwise should widen its board, which is a config change and
133
+ not a code change. Worth revisiting only if a real single-owner instance finds it
134
+ obstructive; the shape of the fix would be a constitution that names the board
135
+ wider, not a bootstrap exception.
136
+
137
+ ## 6. Discord is unchanged
138
+
139
+ Voting stays hall-only. ADR 0175 §8's argument is untouched by anything here: an
140
+ objection must carry a reason and name the section it faults, and a reaction cannot
141
+ express that. The owner re-affirmed this on 2026-08-25 when asked directly. #1003271
142
+ improves the *notification* and leaves the ballot where it is.
@@ -267,4 +267,6 @@ This keeps the decision history honest and traceable.
267
267
  | 0189 | [**A frozen baseline must justify itself: declared floors and the freeze-forever detector** ([task 1003210](https://cloudbongos.com/builders#/task/1003210), goal 1000079). The shrink-only ratchet makes a quality metric unable to REGRESS and, by the same design, unable to sit still noticeably: the follow-up audit measured the whole file at "every metric equal to baseline, 0 reduced" and the ratchet reported a clean PASS, because one green covers both actively-paying-down and nobody-has-looked-in-a-year. The ratchet has no concept of time; this adds it. **Age comes from git, never a stored `since:` field** — a timestamp beside each metric would be faster and hermetic and would also be the field that silently stops being updated, leaving old debt permanently fresh-looking, which is worse than no check because it produces confident green; an unavailable history reports UNKNOWN, not fresh (the `--require-roster` doctrine). A metric's age is the date of the OLDEST commit still carrying its current value, so a baseline recorded once and never revisited dates to its introduction — exactly the case being hunted. **The half that matters is the declared floor.** The naive "flag anything unmoved for N weeks" fires forever on metrics that are FINISHED (`require_cycle_count`'s four deliberate lazy requires; the ~28 duplicate windows ADR 0091 §2 forces because a module may not import core), and a check that cries wolf weekly is one everyone learns to skip — the same alert-fatigue failure ADR 0188 §2.4 avoids. So a metric may declare `floors: {name: {value, why}}` and a metric AT its floor is done, not stale; the `why` is mandatory and length-checked, because a floor exempts a metric from the clock permanently and must therefore be a written claim someone can disagree with — **silence was the problem**, and a floor without a reason is just a mute button. A floor exempts only DOWN TO the floor, so `duplicate_window_count`'s 59-vs-28 keeps its ~31 reducible windows on the clock. **Two severities, split by whose fault it is:** declaration rot (a floor naming a dead metric, a floor with no reason, a floor the metric already dropped below) is hermetic and caused by the diff at hand, so it HARD-FAILS the merge gate; staleness is nobody's diff and would punish whoever pushed next, so it warns in fitness and is FILED weekly by the patrol — the "repeatable, scheduled version" the audit asked for. Also fixed a latent eater: `--update-baselines` wrote a fixed object literal, so one refresh destroyed the accumulated `_comment` and (once floors landed) every floor, deleting a finished metric's justification from the repo while turning it into permanent weekly noise. Nothing is flagged today — the file is six days old — so the detector is verified by shrinking the window, not by waiting. Rejected: a stored `since:`; no exemptions; a floor with no required reason; hard-failing on staleness; patrol-only placement.](<redacted>.md) | quality ratchets / debt governance |
268
268
  | 0190 | [**A changed project setting reaches a running instance by an in-place env patch and a restart — never a template re-write** ([task 1003140](https://cloudbongos.com/builders#/task/1003140), goal 1000058). Task 1003139 made a NEW instance read `platform_visibility` / `joinability` from the web.env the runner writes; a RUNNING instance loads its branding pack once, memoized and deep-frozen for the process lifetime, so a changed value could not reach it at all — the settings were write-only state on the platform. Decision: `PATCH /provisioning/instances/:id/settings` writes the platform row first, then for an ACTIVE instance on a shape the runner can reach enqueues a `settings-apply` intent (migration provisioning_010); the control-plane runner reads the instance's web.env as it stands, upserts ONLY the two settings lines (`upsertEnvVars`), and RESTARTS the service. The in-place patch is the load-bearing choice: the natural move — re-running `webEnvBody` as domain-attach does — regenerates the file from the row, and the row does not know the OAuth client secret or the App private key that `placeManifestCreds` placed later, so a template re-write silently blanks sign-in on the very instance the owner just touched. `SETTINGS_APPLY_FROM = {active}` because a pre-active instance gets the same values at standup and a torn-down one has nothing to restart (the provision intent cannot stand in — it excludes `active`, task 1002691); `SETTINGS_APPLY_SHAPES = {co-tenant, standalone}` because a dedicated droplet has no post-hoc exec path, and a push that cannot land is refused at the route AND the runner rather than reported. The response is a receipt, not a claim: it says the restart is coming and that the instance keeps its current setting until then, and the read-back lives on the instance's own public `GET /instance` manifest, whose `brand.project` now carries the resolved policy (`clientBranding` allow-list widened deliberately). Rejected: the template re-write; re-provisioning as the push; skipping the restart (impossible while the pack is memoized); claiming the new state on PATCH return.](<redacted>.md) | provisioning / settings channel |
269
269
 
270
+ | 0191 | [**Majority, and an author who may vote but never decide alone** ([task 1003273](https://cloudbongos.com/builders#/task/1003273); diagnosis and companion in [task 1003271](https://cloudbongos.com/builders#/task/1003271)). Supersedes ADR 0175 §3's pass-rule set and §§7/11's flat anti-self rule. The owner reported voting as broken; nothing was broken. Nine items sat open under `first_ratifier` with four archons active — any one of three non-authors could have ratified any of them in a click — but every item was authored by the one person looking at them, and ADR 0175's anti-self rule was enforced by rendering NO BALLOT, so from the owner's seat the feature did not exist. The lesson: a rule enforced by removing the control is indistinguishable from a broken feature. Two decisions. (1) `majority` joins PASS_RULES: strictly more than half of the resolved MEMBERSHIP must vote yes — of the membership, not of the votes cast, so low turnout cannot quietly pass things; a tie is not a majority. The three rules are NOT a ladder of strictness, they differ on WHAT SILENCE MEANS — `first_ratifier` and `consent` pass an unvoted item, `majority` returns it — which is the axis that matters and now the comment in config.js. (2) An author MAY vote and may NEVER DECIDE ALONE: the rule moved from refusing the cast to the two places something is actually decided — `shouldCloseAfterVote` needs a non-author vote under every rule, and the majority tally needs a non-author YES. Their yes still counts toward the numerator; their `no` is excluded from the objection test (else silence would be an author's safest act); a self-cast earns no karma. The ADR 0175 §7 amendment carve-out survives as the author rule's SCOPE, not an exception: `authorRuleApplies` is full_idea-only, so a sole Archon still ratifies their own amendment. Also records why the CLOCK is a consequence of majority and not an independent fix — `tallyOutcome` passes on ZERO votes, so adding a deadline under `first_ratifier` would have closed all nine items as ratified-unread, paying karma and credits on ideas nobody read; `BOARD_DEFAULTS.window_minutes` therefore stays `null` in the neutral core. States the cost plainly: three affirmative votes are needed on a four-member board, and a solo instance still cannot ratify its own ideas. Discord stays outbound-only (ADR 0175 §8 re-affirmed).](<redacted>.md) | government / board room |
271
+
270
272
  > ⚠️ **Numbering collision (known):** two ADRs share `0008`. The numbers were assigned in parallel sessions before the lock could be enforced. The files are kept under their existing filenames to preserve git history, but the table disambiguates them as `0008-a` / `0008-b`. New ADRs should resume at `0040` to avoid further collisions. (`0024` is also a real on-disk duplicate: the cloneable-repo/local-first-memory ADR and the multi-agent-system-architecture ADR were both numbered `0024` in parallel; both keep their filenames, disambiguated as `0024-a` / `0024-b` like `0008`. Likewise `0025` ended up with two on-disk files — the offsite-backup ADR and the structured-criterion↔task-link ADR — both kept under their existing filenames and disambiguated as `0025-a` / `0025-b`; separately, the BFG ADR had also claimed `0025` on 2026-05-30 and yielded to `0026`. A third happened 2026-06-06: `0036` was claimed in parallel by the builders-subdomain ADR ([#726](https://example.com/builders#/task/726)) and this Discord-channels ADR ([#727](https://example.com/builders#/task/727)); the Discord-channels ADR yielded to `0037`. A fourth landed 2026-06-10: `0044` was claimed in parallel by the palette-replacement ADR and the per-box game-preview ADR ([#897](https://example.com/builders#/task/897)); both keep their filenames, disambiguated as `0044-a` / `0044-b` like `0008`. A fifth was caught pre-land 2026-06-13: `0050` was claimed in parallel by the device-flow-admission ADR ([#1000](https://example.com/builders#/task/1000)) and the sandbox-for-everyone ADR ([#1001](https://example.com/builders#/task/1001)); the latter yielded to `0052` before merging, so there is no `0050` duplicate. A sixth was caught pre-land 2026-06-21: the tool-agnostic-design-layer ADR ([#1375](https://example.com/builders#/task/1375)) first collided at `0079` with the value-gated-autonomous-engagement ADR, then at `0080` with the llm-cache-api-transport ADR (both part of the 0075–0080 LLM-cost-reduction series, landed on `main` in parallel); it yielded to `0081` before merging, so there is no `0079`/`0080` design-layer duplicate. A seventh landed 2026-06-23: `0084` was claimed in parallel by the auto-derived-per-task-rank-floor ADR and the remove-default-spend-cap ADR (task 1499); the rank-floor ADR landed on `main` first, so the spend-cap ADR yielded to `0085`, leaving no `0084` duplicate. An eighth landed 2026-06-27: `0095` was claimed in parallel by the borrowed-memory-and-retrieval-concepts ADR (PR #459, landed first) and the cross-agent-context-management ADR ([#1651](https://example.com/builders#/task/1651), PR #460); both keep their filenames, disambiguated as `0095-a` / `0095-b` like `0008`. These two are companions — `0095-a` adopts the memory/retrieval concepts and explicitly defers the grep-vs-semantic question to `0095-b`.)