@bongos/core 1.19.339 → 1.19.341

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.339",
6
- "core_contract": "1.19.339",
7
- "source_commit": "2fec46ecdc4aff13a2b42b6dd37e7151fa7a7c6c",
5
+ "core_version": "1.19.341",
6
+ "core_contract": "1.19.341",
7
+ "source_commit": "395ad8796967381d04b9f5ad9247da0cbf60d419",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-25T01:23:32.854Z",
9
+ "built_at": "2026-08-25T01:41:39.768Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 325,
12
+ "docs_redacted": 326,
13
13
  "agent_docs_stubbed": 22,
14
- "functional_verbatim": 1483,
14
+ "functional_verbatim": 1485,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1830,
20
- "tree_sha256": "7a407b4e4d5c929c91502bf31d949fb6bbf8634ce0a2ea6caa9b7dd48ddfbdd9",
19
+ "file_count": 1833,
20
+ "tree_sha256": "29b405f9216626b6f363e10be67e90f2bde67fdd472151fc4eaf0a6c18384acd",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -1419,10 +1419,15 @@
1419
1419
  "mode": "0000644",
1420
1420
  "sha256": "d59906a447f2459efdef6c3eb97cf8629813ec74776ead700017f02ef76aed9e"
1421
1421
  },
1422
+ {
1423
+ "path": "docs/adr/0188-patrol-runs-on-ci-not-the-server.md",
1424
+ "mode": "0000644",
1425
+ "sha256": "4feacf7bc809cdff1a23a1ef5b57d5d28aa448b80bd1ad24fc70860c0fe6739f"
1426
+ },
1422
1427
  {
1423
1428
  "path": "docs/adr/README.md",
1424
1429
  "mode": "0000644",
1425
- "sha256": "87606f8e07f470f0ff55d7ac733de9d1004d3d15eddf1003a8958566b4147077"
1430
+ "sha256": "4c736c015e635ed0b4e78445f35acbd8b84a14708634e5caf4a957357b1e22b9"
1426
1431
  },
1427
1432
  {
1428
1433
  "path": "docs/api-reference.md",
@@ -1457,12 +1462,12 @@
1457
1462
  {
1458
1463
  "path": "docs/copy-inventory.md",
1459
1464
  "mode": "0000644",
1460
- "sha256": "5468f593a35bdbe5d655b79d578f81b6dde532af6f32e48653c4a91066f3dee3"
1465
+ "sha256": "a47b42bd38656c5aa947d278ecf4b82cdf7113d94ac6c6796f8b7ecada6030e5"
1461
1466
  },
1462
1467
  {
1463
1468
  "path": "docs/copy-registry.json",
1464
1469
  "mode": "0000644",
1465
- "sha256": "93d1091d0acf93aeede8ac44552780b58dd78174c46a076215f171e4af99cc7f"
1470
+ "sha256": "11d91419272c08d96af33fc4a9df963b3f4c249874e2bfebd32396a40ac275e5"
1466
1471
  },
1467
1472
  {
1468
1473
  "path": "docs/design-contract.md",
@@ -1572,7 +1577,7 @@
1572
1577
  {
1573
1578
  "path": "docs/file-map.md",
1574
1579
  "mode": "0000644",
1575
- "sha256": "ffd052f893ea0ef47a65e66a6d260fd43c1ccc653fa277f11f13805099e73d45"
1580
+ "sha256": "bbe32c152ba31a68953f318f9a94e010252e70a6e076d64d7dca820f5c19777b"
1576
1581
  },
1577
1582
  {
1578
1583
  "path": "docs/handoff-template.md",
@@ -1772,7 +1777,7 @@
1772
1777
  {
1773
1778
  "path": "docs/recipes/redteam-patrol.md",
1774
1779
  "mode": "0000644",
1775
- "sha256": "3dbdc33eb1aa68da5e6569e836845f09fd14953d94794e005af67ce517fb6dad"
1780
+ "sha256": "656f30f568a56111ce6c2db8f3d61b0f9df3b168f41388b953a4552144579c94"
1776
1781
  },
1777
1782
  {
1778
1783
  "path": "docs/recipes/search-index-ingestion.md",
@@ -4727,12 +4732,12 @@
4727
4732
  {
4728
4733
  "path": "package-lock.json",
4729
4734
  "mode": "0000644",
4730
- "sha256": "d422f518f25f5a4063fa0f114cb86ee4b501e8362e8c0f0f526707d8d6ec137d"
4735
+ "sha256": "7c3952267fd38da7241791d8f5c4e7a2d8226d4cf8d30473fd6fe56ca000a4af"
4731
4736
  },
4732
4737
  {
4733
4738
  "path": "package.json",
4734
4739
  "mode": "0000644",
4735
- "sha256": "4988c346e6258b2eca07502ffdf4991652f381983258a2a13de47dc4cafc72b6"
4740
+ "sha256": "d2ed11ab8e90eb8293778ce19fe921737158c0ab9d8063a9842383b1cfc59e94"
4736
4741
  },
4737
4742
  {
4738
4743
  "path": "public-docs/index.html",
@@ -4972,7 +4977,7 @@
4972
4977
  {
4973
4978
  "path": "public-landing/assets/cosmos.css",
4974
4979
  "mode": "0000644",
4975
- "sha256": "2a258be2790a6fadd1c2d9e190675c62e2b68dddece65f497b4e983c6d9b9146"
4980
+ "sha256": "0853f9974cf971b28022bbbaef2e5bc13ea2ae50363e6141b0d7bb4638e4bdf8"
4976
4981
  },
4977
4982
  {
4978
4983
  "path": "public-landing/assets/verro-medallion.png",
@@ -4992,7 +4997,7 @@
4992
4997
  {
4993
4998
  "path": "public-landing/projects.html",
4994
4999
  "mode": "0000644",
4995
- "sha256": "44474703a87cecfa8f80ecbca6ecfb03d4fc3ccf166fe26bc4c310f404f5f76a"
5000
+ "sha256": "208f23e9909da075d0f2bb64732ef34253421615fc6c7c6142eb3605637821f3"
4996
5001
  },
4997
5002
  {
4998
5003
  "path": "public-landing/world/assets/clouds/cloud-island-large.png",
@@ -5262,7 +5267,7 @@
5262
5267
  {
5263
5268
  "path": "scripts/gds/control-manifest.js",
5264
5269
  "mode": "0000644",
5265
- "sha256": "189caa1e5c263beb5d2c251d924abfe6ea84b7f8b26409a93633c8466bd5801c"
5270
+ "sha256": "6c9538a110e45a184531e3ddd7725a8307da823411ef71f0d0b575dd3ee9571b"
5266
5271
  },
5267
5272
  {
5268
5273
  "path": "scripts/gds/copy-inventory.js",
@@ -5407,7 +5412,7 @@
5407
5412
  {
5408
5413
  "path": "scripts/gds/fitness.js",
5409
5414
  "mode": "0000644",
5410
- "sha256": "0181ba9d5595199777a84b1bc8363d5cda55df68c1eeecb647a18c4fbb6697df"
5415
+ "sha256": "925006ed968209632b20677d05594158ebc27e65229d96a7d69a53baf93f6ed0"
5411
5416
  },
5412
5417
  {
5413
5418
  "path": "scripts/gds/gate-review.js",
@@ -5772,7 +5777,7 @@
5772
5777
  {
5773
5778
  "path": "scripts/gds/redteam-patrol.js",
5774
5779
  "mode": "0000644",
5775
- "sha256": "4e4ce11a67de653550851d9e0b9931253c8b100dd4285f2a39c74ba2f932b23c"
5780
+ "sha256": "7460ee330a6bc53176a6ab83cb1019d50fe7d872d8a5bf31486e87edc86059af"
5776
5781
  },
5777
5782
  {
5778
5783
  "path": "scripts/gds/regen-instance-docs.js",
@@ -5799,10 +5804,15 @@
5799
5804
  "mode": "0000644",
5800
5805
  "sha256": "084fa5c4da1e755fa0edde5ec770e5151a9f66a247bca222bc7a2366ccf61748"
5801
5806
  },
5807
+ {
5808
+ "path": "scripts/gds/routine-schedule.js",
5809
+ "mode": "0000644",
5810
+ "sha256": "0d75c6b9be5a67738c91e55cda9084e10d7c64aa49c80ce9c0fa4219afcbd14c"
5811
+ },
5802
5812
  {
5803
5813
  "path": "scripts/gds/run-routine.js",
5804
5814
  "mode": "0000644",
5805
- "sha256": "275098d7069661885fcfc6470d690e4fd18134c475e3d03cb4361a7183f9de08"
5815
+ "sha256": "8b7765f52f4d6e6559a47bbc911bf5edc4acea81a3765b8811be9fd07be8dfb7"
5806
5816
  },
5807
5817
  {
5808
5818
  "path": "scripts/gds/run-unit-tests.js",
@@ -6557,7 +6567,7 @@
6557
6567
  {
6558
6568
  "path": "src/module-api.js",
6559
6569
  "mode": "0000644",
6560
- "sha256": "930f33a07876e76cf85203ff41de06eb1eebe120508946991f2ad0c73bd0a99c"
6570
+ "sha256": "96c4cf1842a773983825fb0a8639b3e73eb0a3e7646a38781962530f072f6457"
6561
6571
  },
6562
6572
  {
6563
6573
  "path": "src/module-loader/catalog.js",
@@ -8422,12 +8432,12 @@
8422
8432
  {
8423
8433
  "path": "tests/projects_hub.mjs",
8424
8434
  "mode": "0000644",
8425
- "sha256": "211aa868b86a2b663f5525c3d2ddfa406498d608363d28c0786219d3c0b6e249"
8435
+ "sha256": "c6062fe1a27ef5797e82e706e85034570baa933cca1d1db041b40bd3f5517a0b"
8426
8436
  },
8427
8437
  {
8428
8438
  "path": "tests/projects_hub_pre_uat.mjs",
8429
8439
  "mode": "0000644",
8430
- "sha256": "c30221f0cb0b23b7f18b2edffd26cb895038543b07ee87c7a401508131edfc1a"
8440
+ "sha256": "d1f8e7c790b3086e15d727c38738e2ee8a08dc439a5b0d19ec00f988754fd8df"
8431
8441
  },
8432
8442
  {
8433
8443
  "path": "tests/promote_goal_id.mjs",
@@ -8567,7 +8577,7 @@
8567
8577
  {
8568
8578
  "path": "tests/redteam_patrol.mjs",
8569
8579
  "mode": "0000644",
8570
- "sha256": "bf32ae5c734fc95873433b7c4e251a6b166c167ccc91c4684d061b97681c3357"
8580
+ "sha256": "2dbaca4570cdac06c9ebdf2447b9955edeb1afdd763f24d98faec2103001a9e9"
8571
8581
  },
8572
8582
  {
8573
8583
  "path": "tests/regrade_eligibility.mjs",
@@ -8619,6 +8629,11 @@
8619
8629
  "mode": "0000644",
8620
8630
  "sha256": "b56a6945713e738542d612f6d43dd9bd00ce1a370b3d7b3e130f4e59a056ee15"
8621
8631
  },
8632
+ {
8633
+ "path": "tests/routine_schedule.mjs",
8634
+ "mode": "0000644",
8635
+ "sha256": "ba7f5690b9a3649a4c4cd2571d316aa837ffc01874d57aaa418dc72683c6a1f1"
8636
+ },
8622
8637
  {
8623
8638
  "path": "tests/routine_timers.mjs",
8624
8639
  "mode": "0000644",
@@ -8632,7 +8647,7 @@
8632
8647
  {
8633
8648
  "path": "tests/run_routine.mjs",
8634
8649
  "mode": "0000644",
8635
- "sha256": "b27259a2ffa07f800d5e74aa6d35b623ecd7de2118f63fe4274e105bbbe81daf"
8650
+ "sha256": "a33afdf5b7cbe592f77d5162ad8129f449efb0872ed2b484725c6d1bf0ba27c7"
8636
8651
  },
8637
8652
  {
8638
8653
  "path": "tests/run_routine_two_root.mjs",
@@ -0,0 +1,162 @@
1
+ # 0188 — The weekly patrol runs on CI, and a declared cadence must have something that fires it
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-08-24
5
+ - **Tasks:** [#1003209](https://cloudbongos.com/builders#/task/1003209) (goal 1000079 — pay down the frozen debt and make enforcement durable; criterion C4 `reduction-durable`)
6
+ - **Builds on:** [ADR 0078](<redacted>.md) (deterministic-first routines + the `run-routine.js` dispatcher), [ADR 0180](<redacted>.md) §3 (the patrol's dependency checks), [ADR 0022](0022-secrets-policy.md) (the prevent → gate → patrol layering)
7
+ - **Related:** [ADR 0162](<redacted>.md) (patrol findings are advisory — verdicts never self-execute), [ADR 0093](<redacted>.md) §1 (where `routine-timers.js` landed — the in-process poller seam this deliberately does *not* use)
8
+
9
+ ## 1. Context
10
+
11
+ Goal 1000076 shipped a weekly red-team patrol: `scripts/gds/redteam-patrol.js`
12
+ (full-git-history secret scan, dependency-drift review, workflow action-pin
13
+ check), the `.claude/scheduled-tasks/redteam-weekly` routine that describes how
14
+ to run it, a recipe, and a row in `control-manifest.js` asserting the control
15
+ still exists.
16
+
17
+ Nothing ever ran it. The follow-up audit
18
+ ([`docs/audits/ai-dev-redteam-followup.md`](../audits/ai-dev-redteam-followup.md) §7)
19
+ put the failure in three parts:
20
+
21
+ 1. **No committed scheduler** for `.claude/scheduled-tasks/` at all. ADR 0078
22
+ built the external dispatcher an operator was meant to wire into cron; the
23
+ wiring never happened, which `routine-timers.js`'s own header already admits
24
+ in so many words — *"nothing was ever durably scheduled."*
25
+ 2. **No cadence to read.** The SKILL.md declared `mode:` and `steps:` — *how* to
26
+ run — but never *how often*. There was nothing for a scheduler to honour even
27
+ if one had looked.
28
+ 3. **A manifest that was green anyway.** `control-manifest.js` proved the patrol
29
+ "wired" by finding the substring `redteam-patrol.js` inside the SKILL.md —
30
+ i.e. the routine mentioning its own scanner. The control read as connected
31
+ for months while it ran zero times.
32
+
33
+ This is the same shape the ADR 0022 audit found in the first place: a control
34
+ that can quietly stop existing (or, here, quietly never start) is not a control.
35
+ It is also the fifth instance in this goal of a more specific pattern — **an
36
+ enforcement surface that names a file goes VACUOUS, not red, when the thing it
37
+ names moves or stops.**
38
+
39
+ ## 2. Decision
40
+
41
+ ### 2.1 The runner is CI, not the always-on server
42
+
43
+ The obvious symmetry was to add the patrol beside `peer-vote-tally` in
44
+ `modules/autonomy/pollers/routine-timers.js` — the in-process timer built (as an
45
+ ADR 0078 amendment) for exactly the "deterministic routine nobody scheduled"
46
+ problem, whose header even reasons through why an in-process timer beat external
47
+ cron *there*.
48
+
49
+ **That reasoning does not transfer, and applying it here would have produced a
50
+ patrol that reports clean while scanning nothing.** All three checks read things
51
+ only the *repository* has:
52
+
53
+ | check | needs |
54
+ |---|---|
55
+ | history-secrets | the full git history |
56
+ | action-pins | `.github/workflows/` |
57
+ | dependency-review | a `package.json` + `package-lock.json` npm can audit |
58
+
59
+ A deployed instance runs a **vendored** core out of `node_modules/@bongos/core`.
60
+ That directory is not a git repo, carries no workflows, and ships no lockfile.
61
+ The timer would have found no history to scan, no workflows to check, and
62
+ nothing to audit — and every one of those degrades to *"nothing found"*. Worse
63
+ than unscheduled: confidently, weekly, wrong.
64
+
65
+ So the scheduler is `.github/workflows/redteam-patrol.yml` — a weekly GitHub
66
+ Actions lane with `fetch-depth: 0` and a pinned, checksum-verified gitleaks
67
+ binary (the missing dependency the audit listed as its own gap). It invokes
68
+ `run-routine.js redteam-weekly` rather than the patrol script directly, so the
69
+ SKILL.md's `steps:` remain the single source of truth for what a run does.
70
+
71
+ ### 2.2 `cadence:` is a real frontmatter field
72
+
73
+ `parseRoutineFrontmatter` now reads `cadence: hourly|daily|weekly|monthly`. It
74
+ is **declarative only** — `run-routine.js` still runs a routine exactly once per
75
+ invocation. The cadence is the contract a committed scheduler must honour.
76
+
77
+ ### 2.3 A cadence nothing fires is a red build
78
+
79
+ `scripts/gds/routine-schedule.js` (fitness Check 25) is the part that keeps
80
+ §7 from recurring. Its predicate is **relational, not existential**: it reads
81
+ the cadence each routine declares, reads the cron each scheduler carries,
82
+ classifies that cron into the period it actually realizes, and fails when the
83
+ two disagree in either direction —
84
+
85
+ - a routine declares a cadence and no scheduler fires it → red
86
+ - a scheduler dispatches a routine that no longer exists → red (rename rot)
87
+ - the cron's real period differs from the declared cadence → red
88
+
89
+ The third is the one that matters most, because **no existence or substring
90
+ predicate can ever see it.** Edit the cron from weekly to monthly and the file
91
+ still exists, the workflow still names the routine, and every "is it wired?"
92
+ check in the repo stays green.
93
+
94
+ The manifest row was re-pointed to match: "wired" for a scheduled control now
95
+ means the *scheduler* names the routine, not that the routine names itself.
96
+
97
+ ### 2.4 Red means "a finding was lost", not "a finding exists"
98
+
99
+ A CI runner has no GDS session, so `--file` would have hit `cli-lib`'s
100
+ interactive 401 path — printing *"run /builder-reauth"* and calling
101
+ `process.exit(2)` into a cron log, indistinguishable from a crash, before the
102
+ findings were ever shown. `--file` now preflights the session and follows the
103
+ `main-audit.js --require-roster` doctrine: report loudly rather than
104
+ green-and-vacuous.
105
+
106
+ The exit code is a small table (`disposition()`), and the choice inside it is
107
+ deliberate. Findings are **advisory** (ADR 0162) — they are filed for
108
+ adjudication, not acted on — so a run that finds three things and files them is
109
+ a **success**:
110
+
111
+ | state | exit |
112
+ |---|---|
113
+ | ran, nothing to file | 0 |
114
+ | ran, dry run | 0 |
115
+ | ran, findings **filed** | 0 |
116
+ | ran, findings it **cannot** file | **2** |
117
+
118
+ Red is reserved for the single state where evidence evaporates. The alternative
119
+ — failing whenever findings exist — would have gone red on the very first run
120
+ and stayed red: the staging credential in commit `092e473` is still in history,
121
+ unrotated, so the history scan has a standing hit. A weekly job that is always
122
+ red is a weekly job nobody reads, which is the failure this whole goal is about.
123
+
124
+ Filing itself is optional and credential-gated: with no `GDS_PATROL_SESSION`
125
+ secret the patrol still runs every week and writes findings to the job summary.
126
+ That default matters because this same goal's C1 is about *removing* long-lived
127
+ tokens — the patrol must not require a new one to be useful.
128
+
129
+ ## 3. Consequences
130
+
131
+ - The patrol runs unattended for the first time. The first scheduled runs are
132
+ expected to file a `history-secrets` report until commit `092e473` is scrubbed.
133
+ - Adding a routine to `.claude/scheduled-tasks/` now forces a choice: declare a
134
+ cadence and schedule it, or declare none and accept a warning that it is
135
+ manual-only. Silence is no longer an option.
136
+ - The check is tolerant of the two-repo split: `.claude/scheduled-tasks/` is
137
+ materialized into every instance, `.github/workflows/` is the core repo's and
138
+ does not travel — so in a checkout with no workflows directory, unscheduled
139
+ routines are warnings, not violations.
140
+ - `cronCadence()` refuses step values, lists and ranges rather than guessing.
141
+ An exotic schedule cannot be described by a cadence word and must be reworked
142
+ or left manual — the safe direction, since a clever cron silently counting as
143
+ `weekly` would reopen the drift this exists to catch.
144
+
145
+ ## 4. Alternatives rejected
146
+
147
+ - **An in-process timer in `routine-timers.js`.** §2.1 — the server cannot see
148
+ the evidence. Rejected on correctness, not cost.
149
+ - **Teaching `routine-timers.js` to discover `.claude/scheduled-tasks/` and
150
+ honour `cadence:` generically.** The task offered this first and it is the more
151
+ general mechanism, but its only candidate user cannot run there, so it would
152
+ have been a scheduler built for nobody. The `cadence:` field is deliberately
153
+ *scheduler-agnostic*, so an instance that later wants an in-process lane for a
154
+ server-side routine can read the same declaration.
155
+ - **Failing the job whenever findings exist.** Permanent red, then ignored. §2.4.
156
+ - **Requiring a GDS session secret.** Would have made the whole lane inert until
157
+ a credential was provisioned, and adds a long-lived token to a goal whose C1 is
158
+ about eliminating one.
159
+ - **A deeper `control-manifest.js` predicate instead of a new check.** The
160
+ manifest's shallowness is deliberate and documented (existence + one wiring
161
+ substring); the cadence↔cron agreement is relational and does not fit that
162
+ shape. Re-pointing the row *and* adding the check keeps each honest.
@@ -261,5 +261,6 @@ This keeps the decision history honest and traceable.
261
261
  | 0185 | [**The spark-handoff credit split: 70% developer, 30% spark author** ([task 1002923](https://cloudbongos.com/builders#/task/1002923), BV1.R147, goal 1000062 — completing the chain [task 1002920](https://cloudbongos.com/builders#/task/1002920) mark → [task 1002921](https://cloudbongos.com/builders#/task/1002921) queue → [task 1002922](https://cloudbongos.com/builders#/task/1002922) pickup). [ADR 0172](<redacted>.md) built the ideator streams and paid `captured_by`, the only person an idea then had. The spark handoff created a second: R146 develops a spark IN PLACE, preserving the author while recording a `developed_by`. **R146 therefore shipped paying NOTHING on purpose** — not an oversight but a refusal to guess, because `awardIdeaFullPassCredit`'s idempotency predicate is `(builder_id, reason)`, so paying the DEVELOPER would not have blocked a later payment to the AUTHOR and one idea would have paid twice; the cost of that caution was that a developed Full Idea received stream 2 **never** unless its author resubmitted. Owner decision (2026-08-22): **70/30 favouring the developer**, on BOTH existing streams, one ratio so there is one rule to explain. The reasoning is behavioural, not sentimental — developing someone else's spark is the scarce act the queue depends on, and at an even split it pays HALF what authoring your own Full Idea pays for the same writing work, so the rational move is always to write your own and the R145 queue stays a list nobody reads. The author keeps 30% plus their share of stream 1 on every task shipped under the idea, forever. `IDEA_DEVELOPER_SHARE = 0.70` is a **policy constant in `credits.js`** beside the pass bar and quick factor (ADR 0172's own consequence, the ADR 0023 precedent): a share of someone's earnings deserves a review and a commit, not a runtime edit. **The structural half is what retires R146's caution:** the economy module now derives the payees itself (`ideaCreditParties` reads `captured_by` + `developed_by`), so the caller can no longer choose a recipient — the develop route hands over an idea and a score, never a payee, and books the split like any other path (fail-open; the development has already committed). Three arithmetic properties, each a real bug if missed: the total is **conserved** (developer rounded, author gets the REMAINDER — rounding both pays 6 out of a 5-credit payout); a **self-developed** idea collapses to ONE payee, because two rows for one builder would silently UNDER-pay under the per-builder idempotency predicate, not double-pay (the comparison normalises via `String()` since `bigint` arrives as a string, so a type mismatch cannot become a phantom payee); and **zero-value rows are dropped**. Ids otherwise pass through unconverted — `Number()`-ing a `bigint` risks precision. `builder_id` in the idempotency predicate, which entered as an index optimisation (the R143/IC2 grader finding), is now **load-bearing for correctness**: two payees share one `reason`, so a reason-only predicate would refuse the second, and a partially-booked split completes on the next call. **Not decided here:** stream 3's split (ships with the Board Room, task 1002999), and whether `origin='develop'` in `ideas_task_lineage` belongs to whoever promotes a DEVELOPED idea into a task — ADR 0172 §5 says the row is "set at promotion and at develop (R146)", but R146 creates no task, so that origin stays deliberately unwired rather than invented; it changes who stream 1 pays.](<redacted>.md) | economy / ideator lane |
262
262
  | 0186 | [**Per-project idea templates: the seam is already built; the economics are the open problem** ([task 1002932](https://cloudbongos.com/builders#/task/1002932), BV1.R156, goal 1000062 — a SPIKE, output is this ADR plus a task list, no implementation). The literal question — how does a founder define their own idea shape without a schema change or forking the scorer — is **already answered by shipped work**: R141 gave every template function an optional `registry` defaulting to the built-in one, so validation and scoring both read the declaration as data; `ideas_grades.template_key` is per-row with a format CHECK whose own comment says a project key "stays addressable"; and `provisioning_instances.settings jsonb` is an existing per-project home. **No schema change, no fork.** Decision A: a **`project-settings` kernel port**, sibling of `builder-settings`, read via `resolveOptional` so a vanilla instance with no provisioning module degrades to the built-in registry; `base-v1` stays the default and the fallback for an unresolvable key. Rejected a new `ideas_templates` table (second home for data that has one) and a config file (per-instance, but one instance hosts many projects). **Decision B is the finding that matters.** Completeness is normalised 0–100 WITHIN its template (mean of per-field ratios), while karma reads it against an absolute bar (`IDEA_FULL_PASS_BAR = 60`, `IDEA_FULL_PASS_CREDIT = 5`) — so template choice becomes a credit-minting lever: `base-v1` needs ~200 characters for 5 credits, a 1-field template with `substanceMin` 10 needs ~10, a ~20× arbitrage handed to exactly the projects this ADR empowers. Shipping A without B would make the feature a way to print credits. The fix separates the two questions one number is answering: **completeness stays relative** (it is the right signal for triage ordering and the hall meter) and **karma stops reading it**, switching to an absolute substance total the scorer already computes per field and currently discards after averaging. An interim guard (≥3 required fields, ≥120 total `substanceMin`) bounds the arbitrage without fixing it. Decision C: an optional per-field `weight` (default 1) turning the unweighted mean into a weighted one — today's behaviour is the all-weights-1 case, so it is a data addition and one arithmetic line. Decision D: the five base fields are **not** mandatory (forcing them would make "define your own" cosmetic), replaced by two constraints — a template must declare ≥1 required field, and a stored template is validated at WRITE time so a malformed one is refused there rather than discovered inside the capture path, which must never throw. **Boundary note:** goal 1000062's wall is `[ideas, hall-ui]`, so the port task (provisioning + kernel) and the karma task (economy) cannot be claimed under it.](<redacted>.md) | ideas / templates |
263
263
  | 0187 | [**The OAuth web-handshake cookies live 30 minutes, and move together** ([task 1003234](https://cloudbongos.com/builders#/task/1003234), goal 1000058). The three cookies `/auth/web/start` sets — the CSRF state, the validated return target, the repo-scope opt-in — were 10 minutes each, a hall builder's timescale; a founder's first sign-in can include a GitHub SIGNUP with email verification and came back past ten minutes to "State mismatch" with the return + repo scope gone, and the page's retry door was a bare start. Decision: one `HANDSHAKE_COOKIE_MS = 30 min` for all three (they MUST move together — a state outliving its return cookie lands on the default landing with no scope, a return outliving its state lands on State mismatch), and every OAuth error page plus the access-pending auto-walk-in rebuilds its door from whatever cookies are still valid (`recoveryStartHref`, through the same open-redirect allowlist). What did NOT change: the state is still single-use, still `httpOnly`/`sameSite=lax`/apex-scoped, the code still the bearer — only the window for a legitimate handshake grew. Rejected: lengthening only return + repos (keeps the dead end and breaks the one-lifetime invariant the other way), a server-side state store, sixty minutes. The Discord-link state cookie is a different flow and keeps 10.](<redacted>.md) | auth / security posture |
264
+ | 0188 | [**The weekly patrol runs on CI, and a declared cadence must have something that fires it** ([task 1003209](https://cloudbongos.com/builders#/task/1003209), goal 1000079). Goal 1000076 shipped a weekly red-team patrol, a routine describing how to run it, a recipe, and a manifest row — and nothing ever ran it; the follow-up audit §7 named all three halves (no committed scheduler, no cadence for a scheduler to read, and a manifest green anyway because its wiring predicate was satisfied by the SKILL.md mentioning its own scanner). The symmetric fix — an in-process timer beside `peer-vote-tally` in `routine-timers.js` — is **wrong on correctness, not cost**: all three patrol checks read things only the REPO has (full git history, `.github/workflows/`, an auditable lockfile), and a deployed instance runs a VENDORED core out of `node_modules/@bongos/core` that has none of them, so the timer would have scanned nothing and reported clean — worse than unscheduled. The runner is therefore CI: a weekly Actions lane with `fetch-depth: 0` and a pinned, checksum-verified gitleaks (the missing binary the audit listed separately), invoking `run-routine.js redteam-weekly` so the SKILL.md's steps stay the single source of truth. `cadence:` becomes a real frontmatter field — declarative only, scheduler-agnostic. The load-bearing piece is `routine-schedule.js` (fitness Check 25), whose predicate is **relational, not existential**: it classifies the cron into the period it actually realizes and fails when that disagrees with the declared cadence — the one rot mode no substring or existence check can EVER see, since editing weekly→monthly leaves every "is it wired?" predicate green. The manifest row was re-pointed to match ("wired" now means the SCHEDULER names the routine). Finally the exit code says **a finding was LOST, not that a finding exists**: findings are advisory (ADR 0162), so filing three is a SUCCESS and red is reserved for findings that cannot be recorded — failing on findings would have gone red on run one and stayed there (the unrotated staging credential in `092e473` is a standing history hit), and an always-red weekly job is one nobody reads. Filing is credential-gated but OPTIONAL — with no session secret the patrol still runs and writes its findings to the job summary, which matters because this goal's C1 is about eliminating long-lived tokens, not adding one. Rejected: the in-process timer; teaching `routine-timers.js` generic cadence discovery (a scheduler built for nobody — its only candidate user cannot run there); failing whenever findings exist; requiring the secret; a deeper manifest predicate instead of a new check.](<redacted>.md) | security / enforcement durability |
264
265
 
265
266
  > ⚠️ **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`.)