@bongos/core 1.19.174 → 1.19.176
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 +18 -13
- package/docs/adr/0171-profile-rollup-join-and-consent.md +2 -0
- package/docs/adr/0172-per-craft-compensation-ideator-credit-lane.md +146 -0
- package/modules/economy/credits.js +11 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +5 -1
- package/tests/credit_grant.mjs +30 -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.176",
|
|
6
|
+
"core_contract": "1.19.176",
|
|
7
|
+
"source_commit": "9b6c711a358a67610bdb81e8776bc689a0b9cdd8",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-
|
|
9
|
+
"built_at": "2026-08-16T20:39:00.850Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 306,
|
|
13
13
|
"agent_docs_stubbed": 21,
|
|
14
14
|
"functional_verbatim": 1273,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1600,
|
|
20
|
+
"tree_sha256": "fe346cc282c27c1ec0f17e6dc13d3a82795f14343fa9e42f120b3df9a3138546",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -1332,13 +1332,18 @@
|
|
|
1332
1332
|
{
|
|
1333
1333
|
"path": "docs/adr/0171-profile-rollup-join-and-consent.md",
|
|
1334
1334
|
"mode": "0000644",
|
|
1335
|
-
"sha256": "
|
|
1335
|
+
"sha256": "93a7a996a52b1305e2247fb4b2cf0444ce973712837862b2ecba397e6f2adc71"
|
|
1336
1336
|
},
|
|
1337
1337
|
{
|
|
1338
1338
|
"path": "docs/adr/0172-editable-rank-roles-substrate-axis.md",
|
|
1339
1339
|
"mode": "0000644",
|
|
1340
1340
|
"sha256": "0bfe5ce46139bf9ee236868317cc80ee10eb8747d5606d0dd0fe9f2580ea5177"
|
|
1341
1341
|
},
|
|
1342
|
+
{
|
|
1343
|
+
"path": "docs/adr/0172-per-craft-compensation-ideator-credit-lane.md",
|
|
1344
|
+
"mode": "0000644",
|
|
1345
|
+
"sha256": "d06219f51351c1dd73f7ba556c855e678a2e55e79dacd06419c212a6a24fcc64"
|
|
1346
|
+
},
|
|
1342
1347
|
{
|
|
1343
1348
|
"path": "docs/adr/README.md",
|
|
1344
1349
|
"mode": "0000644",
|
|
@@ -2827,7 +2832,7 @@
|
|
|
2827
2832
|
{
|
|
2828
2833
|
"path": "modules/economy/credits.js",
|
|
2829
2834
|
"mode": "0000644",
|
|
2830
|
-
"sha256": "
|
|
2835
|
+
"sha256": "38526b1d7a095fc48d062ddd645df074f381d2ef0e0aac6593ff306601659904"
|
|
2831
2836
|
},
|
|
2832
2837
|
{
|
|
2833
2838
|
"path": "modules/economy/migrations/economy_001_fix_gds_shipper_description.sql",
|
|
@@ -4222,12 +4227,12 @@
|
|
|
4222
4227
|
{
|
|
4223
4228
|
"path": "package-lock.json",
|
|
4224
4229
|
"mode": "0000644",
|
|
4225
|
-
"sha256": "
|
|
4230
|
+
"sha256": "07c720a5ef0a0e1e93727688e9c6c87e00ab43f289efa7cbb8eebea6f3cdbba9"
|
|
4226
4231
|
},
|
|
4227
4232
|
{
|
|
4228
4233
|
"path": "package.json",
|
|
4229
4234
|
"mode": "0000644",
|
|
4230
|
-
"sha256": "
|
|
4235
|
+
"sha256": "d014a9180d2a66087029d00f9f10da48fbc8ee3be7a88db11280bc35da7fd611"
|
|
4231
4236
|
},
|
|
4232
4237
|
{
|
|
4233
4238
|
"path": "public-docs/index.html",
|
|
@@ -5832,7 +5837,7 @@
|
|
|
5832
5837
|
{
|
|
5833
5838
|
"path": "src/module-api.js",
|
|
5834
5839
|
"mode": "0000644",
|
|
5835
|
-
"sha256": "
|
|
5840
|
+
"sha256": "9459525829ea38080db6cc5e757ef7be130c3da00c6563324e892bf605313d8d"
|
|
5836
5841
|
},
|
|
5837
5842
|
{
|
|
5838
5843
|
"path": "src/module-loader/catalog.js",
|
|
@@ -6372,7 +6377,7 @@
|
|
|
6372
6377
|
{
|
|
6373
6378
|
"path": "tests/credit_grant.mjs",
|
|
6374
6379
|
"mode": "0000644",
|
|
6375
|
-
"sha256": "
|
|
6380
|
+
"sha256": "2b871880fc9e6d636c5fb93d2641abad964f4d3e0ed1e78939b12dc849a7631d"
|
|
6376
6381
|
},
|
|
6377
6382
|
{
|
|
6378
6383
|
"path": "tests/criterion_authoring_gate.mjs",
|
|
@@ -27,6 +27,8 @@ Five facts found during this spike shape the decisions:
|
|
|
27
27
|
|
|
28
28
|
**3. Consent is a fail-closed `profile_state` on `platform_identity_accounts`, with one discriminator.** Migration `<redacted>.sql` (lands with R02) adds `profile_state text NOT NULL DEFAULT 'provisional' CHECK (profile_state IN ('provisional','active'))` and `hide_stats boolean NOT NULL DEFAULT false`. The single rule: **an account is `active` iff it was touched by a hub-session `/sso/authorize`** — the one path where a human is demonstrably at the hub (and where T&C acceptance can later be captured, task 1002913). Device-poll and check-in create `provisional`; the rollup endpoint creates nothing (orphan activity rows are never publicly addressable). Backfill: **all existing accounts start `provisional`** and lazily upgrade on their next hub-session authorize — no join to core `builders` (that join is type-unsafe against `system:*` text ids and would put a core-table read inside a module migration; the price is that existing hub builders' profiles publish on their next sign-in rather than immediately). Public surfaces read only `active` accounts. **R02 ships this fail-closed default without waiting on P03**, because the asymmetry only runs one way: P03 can then only ever *widen* exposure, never claw back something already published. This deliberately narrows the Phase-4 interview's "no extra consent gate" to the accounts whose consent premise actually holds; P03 is the owner either ratifying that boundary (recommended) or reverting to the interview decision verbatim — and the revert branch has a named precondition: check-in/device-created identities have **no self-serve hide path** (no hub builder session exists for them; hub admission is invite-gated), so rejecting P03 first requires an assertion-authenticated hide path or open hub enrollment. Task 1002799 carries this full decision payload.
|
|
29
29
|
|
|
30
|
+
> **P03 outcome — RATIFIED (owner, 2026-08-16, task 1002799).** The fail-closed boundary stands: check-in/device-created accounts stay `provisional` (not publicly listed, not at `/u/<handle>`, not in the sitemap or hub search) until their human signs in at the hub at least once. The owner's rationale: in the intended product flow a builder passes through cloudbongos.com at least once anyway — to find or create the projects they join — so the activation gate is satisfied naturally and costs nothing; the cloudbongos builders hall predating the hub flow is a one-time bootstrap exception, and those accounts lazily activate on their next hub sign-in exactly as the backfill rule above already provides (no backfill activation). Tasks 1002913 (T&C artifact) and 1002914 (criterion text amendments) unblock on this ship.
|
|
31
|
+
|
|
30
32
|
**4. The C2 hide flag is account-global and lives on `platform_identity_accounts.hide_stats` — not in `behavior_prefs`.** Three reasons: the data it guards is account-global hub data, so a per-instance jsonb key would fork the flag per instance; the public profile route must not grow a core→`builder-settings` read dependency that doesn't exist today; and enforcement belongs in the module's read functions (Decision 2), next to the data, where no caller can forget it. (C2's "(builder-settings)" parenthetical is therefore one of the criterion texts task 1002914 amends — the control is still self-serve in hall Settings; only its storage moves.) The write path is an own-scoped `PATCH` route in `platform-identity` (keyed by `req.builder.github_id`, `validateOrRespond`-validated so it needs no `WRITE_VALIDATION_EXEMPT` entry). Public profile responses stay **viewer-independent** — the surviving justification, with edge caching gone (Decision 5), is that a single response shape is the only thing the mirror leak test can actually pin — so the profile page renders the self-toggle client-side by comparing the already-available `/me` identity, and a builder whose stats are hidden inspects their own numbers in Settings via the own-scoped read (the C1 amendment in task 1002914 confirms Settings as the self-view home). Hidden → the track-record section renders the "keeps their track record private" line and **nothing** derived from the rollup ("no partial numbers; a count plus a hidden total is a leak by subtraction"); the local-instance profile keeps its local data (C2's fallback clause applies to the instance render path — the hub has no local data to fall back to). One residual stated honestly: hide removes the hub's *aggregated* view, not the per-instance public numbers it is derived from — every instance's `/public/leaderboard` stays public by design, so a determined reader can still reconstruct a sum the hard way. The C2 amendment text carries that caveat.
|
|
31
33
|
|
|
32
34
|
**5. Hide must beat the cache: when the port resolves (hub), profile responses are `Cache-Control: no-store`.** The route's current `public, max-age=60` would keep serving a builder's stats for up to 60s after they hide — failing C2's "takes effect immediately" and the R06 proof as literally specified (a browser-cached copy survives a reload). On plain instances the existing `max-age=60` stays. The server-rendered `/u` page inherits the same rule for hide-sensitive content (its caching posture is otherwise goal 1000048's call). Cost: hub profile API responses aren't edge-cached — acceptable at current scale (the rollup read is one indexed aggregate per view).
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# 0172 — Per-craft compensation: the ideator credit lane, separate from cost-plus
|
|
2
|
+
|
|
3
|
+
- **Status:** Accepted
|
|
4
|
+
- **Date:** 2026-08-16
|
|
5
|
+
- **Deciders:** Lars (owner / Archon), Claude (Opus 5)
|
|
6
|
+
- **Track:** `internal` (Cloud Bongos — economy)
|
|
7
|
+
- **Task:** BV1.R171 ([#1002995](https://cloudbongos.com/builders#/task/1002995)) — the foundation of the **Ideator compensation** goal ([#1000067](https://cloudbongos.com/builders#/goal/1000067)).
|
|
8
|
+
- **Builds on:** [ADR 0054](<redacted>.md) (cost-plus session reward), [ADR 0146](<redacted><redacted>.md) (`rewardPolicy` + the `cost-plus-only` mode), [ADR 0023](<redacted>.md) (kind multipliers + the 25% idea-promotion slice), [ADR 0120](<redacted>.md) (pay-on-land).
|
|
9
|
+
- **Relates to:** goal 1000062 (three grades of idea — R141/R142/R143 shipped), idea 1000738 (the Board Room).
|
|
10
|
+
- **Gates:** IC2 ([#1002996](https://cloudbongos.com/builders#/task/1002996)) · IC3 ([#1002997](https://cloudbongos.com/builders#/task/1002997)) · IC4 ([#1002998](https://cloudbongos.com/builders#/task/1002998)) · IC5 ([#1002999](https://cloudbongos.com/builders#/task/1002999)) · IC6 ([#1003000](https://cloudbongos.com/builders#/task/1003000)).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Context
|
|
15
|
+
|
|
16
|
+
Cloud Bongos pays **one kind of worker one way**. The equity stream is the
|
|
17
|
+
cost-plus session reward ([ADR 0054](<redacted>.md)):
|
|
18
|
+
`round(true_cost_usd × (1 + margin))`, booked when a shipped session lands. On
|
|
19
|
+
top of that, [ADR 0023](<redacted>.md) pays the
|
|
20
|
+
capturer of a promoted idea a **25% slice** of the resulting task's estimate
|
|
21
|
+
when it ships, and [ADR 0146](<redacted><redacted>.md)
|
|
22
|
+
lets an instance switch to `cost-plus-only`, which **withholds every per-task
|
|
23
|
+
estimate payout — the idea slice included** (it is a fraction of a payout that
|
|
24
|
+
is now zero, so it "cascades to 0", `credits.js:235`). cloudbongos.com runs
|
|
25
|
+
`cost-plus-only`.
|
|
26
|
+
|
|
27
|
+
Two things are now true at once, and they conflict:
|
|
28
|
+
|
|
29
|
+
1. The **ABI model** (goal 1000062, the owner's Holy Workshop Trinity brief)
|
|
30
|
+
makes the **ideator** a first-class craft whose *output is a Full Idea* — a
|
|
31
|
+
deliverable, scored on completeness (R142, shipped). The owner wants that
|
|
32
|
+
craft **paid in credits**, on three streams (below).
|
|
33
|
+
2. On the live instance those credits **cannot be paid**, because they ride the
|
|
34
|
+
very per-task estimate path `cost-plus-only` exists to withhold.
|
|
35
|
+
|
|
36
|
+
The owner resolved it with a principle, 2026-08-16:
|
|
37
|
+
|
|
38
|
+
> *"this should [be] separate from cost plus, an idea-tor should get paid
|
|
39
|
+
> differently than an artist and builder."*
|
|
40
|
+
|
|
41
|
+
Compensation is **per-craft**. A builder earning cost-plus for shipping and an
|
|
42
|
+
ideator earning credits for thinking are **not the same reward being tuned two
|
|
43
|
+
ways** — they are two different lanes. The `cost-plus-only` switch governs the
|
|
44
|
+
*builder estimate* lane; it must not reach across and silence the *ideator*
|
|
45
|
+
lane, because that lane is a different craft's pay.
|
|
46
|
+
|
|
47
|
+
## Decision
|
|
48
|
+
|
|
49
|
+
### 1. Compensation is per-craft; the ideator credit lane is its own reward, exempt from the `cost-plus-only` gate
|
|
50
|
+
|
|
51
|
+
The reward model gains a **named lane per craft**, not a single tunable payout:
|
|
52
|
+
|
|
53
|
+
- **Builder (engineer)** — the cost-plus session reward ([ADR 0054](<redacted>.md)) plus, where `mode !== 'cost-plus-only'`, the per-task estimate ([ADR 0146](<redacted><redacted>.md)). Unchanged.
|
|
54
|
+
- **Ideator** — the **idea credit lane** defined here (three streams, §3). Governed by its **own** policy flag, **never** by `perTaskCredit`.
|
|
55
|
+
- **Artist** — a lane of its own, **deliberately left unspecified here** (the artist craft — grading, copy, voice — is scoped in later ABI work). This ADR reserves the *shape* (a named lane) so the artist lane slots in the same way; it does not define the artist's pay.
|
|
56
|
+
|
|
57
|
+
The rule that makes this real and testable: **no ideator-lane credit is ever
|
|
58
|
+
gated on `perTaskCredit`.** `cost-plus-only` withholds the builder estimate and
|
|
59
|
+
nothing else.
|
|
60
|
+
|
|
61
|
+
### 2. `rewardPolicy` grows an ideator dimension; the gate is separate from `perTaskCredit`
|
|
62
|
+
|
|
63
|
+
`rewardPolicy(rawReward)` (`modules/economy/credits.js`) today returns
|
|
64
|
+
`{ marginPct, usdPeg, mode, perTaskCredit }`, where
|
|
65
|
+
`perTaskCredit = mode !== 'cost-plus-only'`. It gains an **independent** flag:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
ideatorCredit: <resolved from reward config; default TRUE>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`ideatorCredit` is **not derived from `mode`.** An instance in `cost-plus-only`
|
|
72
|
+
still has `ideatorCredit: true` unless it explicitly opts the ideator lane out.
|
|
73
|
+
The ideator-credit writers (IC2/IC4/IC5) gate on `config.ideatorCredit`, never
|
|
74
|
+
on `config.perTaskCredit` — the same shape as the existing per-task gate at
|
|
75
|
+
`credits.js:238`, pointed at a different flag. An instance that wants *no*
|
|
76
|
+
ideator pay sets it false; the default pays.
|
|
77
|
+
|
|
78
|
+
### 3. The three ideator streams (the credit model)
|
|
79
|
+
|
|
80
|
+
All three book to `credit_log` under **ideator-lane reasons** (namespace
|
|
81
|
+
`idea.credit.*`), distinct from `task.shipped`, `task.confirmed`,
|
|
82
|
+
`idea_promotion_bonus`, and the session cost-plus. Idempotency keys are named
|
|
83
|
+
per stream so a re-grade / re-ship / re-vote never double-pays.
|
|
84
|
+
|
|
85
|
+
- **Stream 2 — a passing Full Idea pays 5 credits, once** (IC2, buildable now).
|
|
86
|
+
When a Full Idea (R143) clears the completeness bar, book **5** to its author,
|
|
87
|
+
once, `reason='idea.credit.full_pass'`, idempotent on `(idea_id, reason)`. The
|
|
88
|
+
bar is a completeness threshold set in the reward config (default proposed:
|
|
89
|
+
**60/100** — the R142 score at which four of five base fields are
|
|
90
|
+
substantively answered). Buildable today: R142 + R143 shipped.
|
|
91
|
+
|
|
92
|
+
- **Stream 1 — 5 credits per shipped task under the idea, scaled by idea grade**
|
|
93
|
+
(IC4). As each task descended from an idea (the IC3 lineage) first reaches
|
|
94
|
+
`shipped`, book `round(5 × gradeFactor)` to the idea's author, once per
|
|
95
|
+
`(idea_id, task_id)`, `reason='idea.credit.task_ship'`.
|
|
96
|
+
`gradeFactor = completeness%/100` for a Full Idea; for a **Quick Idea** (no
|
|
97
|
+
completeness score) a flat `QUICK_IDEA_FACTOR` (default proposed **0.6**).
|
|
98
|
+
**This REPLACES the ADR 0023 25% slice** (§4).
|
|
99
|
+
|
|
100
|
+
- **Stream 3 — a board-room-passed idea pays up to 100 × completeness%** (IC5,
|
|
101
|
+
future). When a Full Idea passes the project's governance vote (the Board
|
|
102
|
+
Room, idea 1000738 — *not yet built*), book
|
|
103
|
+
`max(floor, round(completeness% × 100))` to its author, once per idea,
|
|
104
|
+
`reason='idea.credit.governance_pass'`. Owner floor ~**30**. Consumes the
|
|
105
|
+
Board Room's pass event; invents no parallel vote.
|
|
106
|
+
|
|
107
|
+
### 4. The ADR 0023 idea-promotion slice is retired in favour of stream 1
|
|
108
|
+
|
|
109
|
+
The 25% slice (`IDEA_BONUS_FRACTION = 0.25`, `reason='idea_promotion_bonus'`)
|
|
110
|
+
and stream 1 are **two rules for the same thing** — "an idea earns when its work
|
|
111
|
+
ships." Running both would pay an idea twice on every ship. Stream 1 (IC4) is
|
|
112
|
+
the go-forward rule; when IC4 lands it **removes** the 0.25 slice write, so
|
|
113
|
+
there is one idea-credit-on-ship path, not two. Until IC4 lands, the 0023 slice
|
|
114
|
+
stays (harmlessly zero on `cost-plus-only` today). The switch-over is a single
|
|
115
|
+
change inside IC4, guarded so no window pays both.
|
|
116
|
+
|
|
117
|
+
### 5. Two structural facts the streams depend on (recorded, not built here)
|
|
118
|
+
|
|
119
|
+
- **Stream 1 needs one idea → many tasks.** `idea_inbox.promoted_to_task_id` is
|
|
120
|
+
a single FK. IC3 adds the many-tasks lineage — **preferred shape: an
|
|
121
|
+
ideas-owned `idea_tasks` join keyed by task id**, so it needs no `ALTER` on
|
|
122
|
+
the protected `tasks` table and the work stays inside the `ideas` module wall.
|
|
123
|
+
Set at promotion and at develop (R146).
|
|
124
|
+
- **Stream 3 needs the Board Room.** IC5 waits on idea 1000738.
|
|
125
|
+
|
|
126
|
+
## Consequences
|
|
127
|
+
|
|
128
|
+
- **cloudbongos.com can pay ideators on `cost-plus-only`.** The whole reason
|
|
129
|
+
this goal exists. Builders keep earning cost-plus and nothing else; ideators
|
|
130
|
+
earn the idea lane; the two no longer collide. IC6 proves both hold at once.
|
|
131
|
+
- **`rewardPolicy` stays a pure function** — one added field, resolved from
|
|
132
|
+
reward config, unit-testable without the `branding()` cache (the ADR 0146
|
|
133
|
+
pattern). No new table: the thresholds/factors (pass bar, quick factor,
|
|
134
|
+
governance floor) are **policy constants in `credits.js`** for the same
|
|
135
|
+
reasons ADR 0023 kept the kind multipliers in code — they deserve a review +
|
|
136
|
+
ADR + commit trail, not a hot-path UPDATE. A future migration can promote them
|
|
137
|
+
to a table if per-instance tuning is wanted.
|
|
138
|
+
- **Artist pay is deliberately unresolved.** This ADR commits to the *shape*
|
|
139
|
+
(per-craft lanes) so the artist lane lands the same way; it does not price the
|
|
140
|
+
artist craft. That is later ABI work, and pretending to settle it here would
|
|
141
|
+
be a decision made without the design.
|
|
142
|
+
- **The numbers are proposed defaults, not owner-locked.** The pass bar (60),
|
|
143
|
+
quick factor (0.6), and governance floor (30) are this ADR's starting values;
|
|
144
|
+
each is a one-constant change and each downstream task (IC2/IC4/IC5) surfaces
|
|
145
|
+
its own for confirmation. The owner set the *structure* (5 / grade-scaled /
|
|
146
|
+
up-to-100-by-fullness); these fill the gaps the structure left open.
|
|
@@ -159,13 +159,23 @@ const REWARD_MODE_COST_PLUS_ONLY = 'cost-plus-only';
|
|
|
159
159
|
// policy. Exported so a test can assert the mapping without touching the memoized
|
|
160
160
|
// branding() cache (mirrors computeSessionTokenReward's pure/impure split). The
|
|
161
161
|
// derived `perTaskCredit` boolean is what the per-task credit writer gates on.
|
|
162
|
+
//
|
|
163
|
+
// PER-CRAFT COMPENSATION (ADR 0172): `ideatorCredit` is a SEPARATE lane from the
|
|
164
|
+
// builder per-task estimate. It is NOT derived from `mode` — an instance in
|
|
165
|
+
// `cost-plus-only` still pays ideators (ideatorCredit stays true) because that
|
|
166
|
+
// switch governs the builder estimate, not a different craft's pay. The ideator
|
|
167
|
+
// credit writers (IC2/IC4/IC5) gate on `ideatorCredit`, never on `perTaskCredit`.
|
|
168
|
+
// An instance opts the ideator lane out with `reward.ideatorCredit: false`;
|
|
169
|
+
// absent/any-non-false → the default TRUE (a pre-ADR-0172 config keeps paying).
|
|
162
170
|
function rewardPolicy(rawReward) {
|
|
163
171
|
const r = rawReward || {};
|
|
164
172
|
const marginPct = Number.isFinite(Number(r.marginPct)) ? Number(r.marginPct) : REWARD_MARGIN_PCT;
|
|
165
173
|
const usdPeg = Number.isFinite(Number(r.usdPeg)) && Number(r.usdPeg) > 0 ? Number(r.usdPeg) : 1;
|
|
166
174
|
const mode = r.mode === REWARD_MODE_COST_PLUS_ONLY ? REWARD_MODE_COST_PLUS_ONLY : REWARD_MODE_DEFAULT;
|
|
167
175
|
const perTaskCredit = mode !== REWARD_MODE_COST_PLUS_ONLY;
|
|
168
|
-
|
|
176
|
+
// Default-on, opt-out-only: only an explicit `false` withholds the ideator lane.
|
|
177
|
+
const ideatorCredit = r.ideatorCredit !== false;
|
|
178
|
+
return { marginPct, usdPeg, mode, perTaskCredit, ideatorCredit };
|
|
169
179
|
}
|
|
170
180
|
|
|
171
181
|
// Per-instance reward policy, resolved over the code defaults. Best-effort: a
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.176",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.176",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.176",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/src/module-api.js
CHANGED
|
@@ -775,8 +775,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
775
775
|
// landed since 1.19.172 with no explicit bump. run 31968656001. (task 1002620)
|
|
776
776
|
// 1.19.174 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
777
777
|
// landed since 1.19.173 with no explicit bump. run 31969032984. (task 1002620)
|
|
778
|
+
// 1.19.175 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
779
|
+
// landed since 1.19.174 with no explicit bump. run 31970199477. (task 1002620)
|
|
780
|
+
// 1.19.176 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
781
|
+
// landed since 1.19.175 with no explicit bump. run 31971125373. (task 1002620)
|
|
778
782
|
// ---------------------------------------------------------------------------
|
|
779
|
-
const CORE_VERSION = '1.19.
|
|
783
|
+
const CORE_VERSION = '1.19.176'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
780
784
|
|
|
781
785
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
782
786
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
package/tests/credit_grant.mjs
CHANGED
|
@@ -516,6 +516,36 @@ await test('rewardPolicy: unknown/garbage mode → default (a typo never silentl
|
|
|
516
516
|
assert.equal(rewardPolicy(undefined).perTaskCredit, true);
|
|
517
517
|
});
|
|
518
518
|
|
|
519
|
+
// ── ADR 0172: the ideator credit lane is SEPARATE from the builder estimate ──
|
|
520
|
+
|
|
521
|
+
await test('rewardPolicy: ideatorCredit defaults ON, and a pre-0172 config keeps paying ideators', () => {
|
|
522
|
+
assert.equal(rewardPolicy({}).ideatorCredit, true, 'absent flag → the default TRUE');
|
|
523
|
+
assert.equal(rewardPolicy(null).ideatorCredit, true);
|
|
524
|
+
// a config written before this ADR (no ideatorCredit key) must not silently
|
|
525
|
+
// withhold the new lane
|
|
526
|
+
assert.equal(rewardPolicy({ mode: 'cost-plus-and-estimate', marginPct: 20 }).ideatorCredit, true);
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
await test('rewardPolicy: cost-plus-only WITHHOLDS the builder estimate but NOT the ideator lane', () => {
|
|
530
|
+
const p = rewardPolicy({ mode: 'cost-plus-only' });
|
|
531
|
+
assert.equal(p.perTaskCredit, false, 'the builder per-task estimate is off');
|
|
532
|
+
assert.equal(p.ideatorCredit, true, 'the ideator lane is a different craft\'s pay — unaffected');
|
|
533
|
+
// this is the whole point of ADR 0172: the two flags are independent
|
|
534
|
+
assert.notEqual(p.perTaskCredit, p.ideatorCredit);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
await test('rewardPolicy: ideatorCredit is opt-out-only — only an explicit false withholds it', () => {
|
|
538
|
+
assert.equal(rewardPolicy({ ideatorCredit: false }).ideatorCredit, false, 'explicit opt-out');
|
|
539
|
+
// and it is orthogonal to mode: an instance can pay per-task estimate but NOT ideators
|
|
540
|
+
const p = rewardPolicy({ mode: 'cost-plus-and-estimate', ideatorCredit: false });
|
|
541
|
+
assert.equal(p.perTaskCredit, true);
|
|
542
|
+
assert.equal(p.ideatorCredit, false);
|
|
543
|
+
// any non-false value (a typo, a truthy) stays ON — never a silent zeroing
|
|
544
|
+
for (const v of [true, 1, 'yes', undefined, null]) {
|
|
545
|
+
assert.equal(rewardPolicy({ ideatorCredit: v }).ideatorCredit, true, `ideatorCredit=${JSON.stringify(v)} pays`);
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
|
|
519
549
|
await test('rewardPolicy: cost-plus-only still honors a custom margin + peg', () => {
|
|
520
550
|
const p = rewardPolicy({ mode: 'cost-plus-only', marginPct: 35, usdPeg: 2 });
|
|
521
551
|
assert.equal(p.perTaskCredit, false);
|