@bongos/core 1.21.148 → 1.21.149
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 +13 -13
- package/.claude/skills/bongos-feedback/SKILL.md +2 -2
- package/docs/module-api-changelog.md +2 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/gds/feedback-send.js +59 -14
- package/src/module-api.js +1 -1
- package/tests/instance_feedback.mjs +45 -1
package/.bongos-core.json
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
"artifact": "bongos-core",
|
|
3
3
|
"manifest_schema": 1,
|
|
4
4
|
"generator": "scripts/gds/package-core.js",
|
|
5
|
-
"core_version": "1.21.
|
|
6
|
-
"core_contract": "1.21.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.21.149",
|
|
6
|
+
"core_contract": "1.21.149",
|
|
7
|
+
"source_commit": "59efe78dbbc530e6743f98d50abc605e2184adcc",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-10-
|
|
9
|
+
"built_at": "2026-10-19T21:44:07.784Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 608,
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
19
|
"file_count": 3805,
|
|
20
|
-
"tree_sha256": "
|
|
20
|
+
"tree_sha256": "2fb626947478be776e71613c82d973ebf073298ea8d6e5561f4e604e1438d661",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
{
|
|
43
43
|
"path": ".claude/skills/bongos-feedback/SKILL.md",
|
|
44
44
|
"mode": "0000644",
|
|
45
|
-
"sha256": "
|
|
45
|
+
"sha256": "8b295d76d7cc4c9e0f2549db0f1ac857aa8e76a92b3e00898f95820a32c855ba"
|
|
46
46
|
},
|
|
47
47
|
{
|
|
48
48
|
"path": ".claude/skills/bug-triage/SKILL.md",
|
|
@@ -2927,7 +2927,7 @@
|
|
|
2927
2927
|
{
|
|
2928
2928
|
"path": "docs/module-api-changelog.md",
|
|
2929
2929
|
"mode": "0000644",
|
|
2930
|
-
"sha256": "
|
|
2930
|
+
"sha256": "af2424a88f8836e7035c00a52637fbbf137bf1b716130a360868fa50bab54b2d"
|
|
2931
2931
|
},
|
|
2932
2932
|
{
|
|
2933
2933
|
"path": "docs/modules-contract.md",
|
|
@@ -10552,12 +10552,12 @@
|
|
|
10552
10552
|
{
|
|
10553
10553
|
"path": "package-lock.json",
|
|
10554
10554
|
"mode": "0000644",
|
|
10555
|
-
"sha256": "
|
|
10555
|
+
"sha256": "d03c2a175c60400695aecccbe5c9ef87b085521c6bf92176c6d2ed08e514c8a6"
|
|
10556
10556
|
},
|
|
10557
10557
|
{
|
|
10558
10558
|
"path": "package.json",
|
|
10559
10559
|
"mode": "0000644",
|
|
10560
|
-
"sha256": "
|
|
10560
|
+
"sha256": "9d6713f305cdfe492805906778e171c75c42e4fd8dff5935ad9245d185b6ce22"
|
|
10561
10561
|
},
|
|
10562
10562
|
{
|
|
10563
10563
|
"path": "public-docs/index.html",
|
|
@@ -10577,7 +10577,7 @@
|
|
|
10577
10577
|
{
|
|
10578
10578
|
"path": "release-notes.json",
|
|
10579
10579
|
"mode": "0000644",
|
|
10580
|
-
"sha256": "
|
|
10580
|
+
"sha256": "9394c3c784137020e70389b269a68f36f8f2d0757b306a1f64622b8b5886f368"
|
|
10581
10581
|
},
|
|
10582
10582
|
{
|
|
10583
10583
|
"path": "scripts/cron/style_guide_audit.sh",
|
|
@@ -11102,7 +11102,7 @@
|
|
|
11102
11102
|
{
|
|
11103
11103
|
"path": "scripts/gds/feedback-send.js",
|
|
11104
11104
|
"mode": "0000644",
|
|
11105
|
-
"sha256": "
|
|
11105
|
+
"sha256": "e3c82d95c9412126d0c064d40267c7ae7befbc3cebdba99c496c884dceb4a750"
|
|
11106
11106
|
},
|
|
11107
11107
|
{
|
|
11108
11108
|
"path": "scripts/gds/fetch-art-key.js",
|
|
@@ -12957,7 +12957,7 @@
|
|
|
12957
12957
|
{
|
|
12958
12958
|
"path": "src/module-api.js",
|
|
12959
12959
|
"mode": "0000644",
|
|
12960
|
-
"sha256": "
|
|
12960
|
+
"sha256": "b89f1cd4820d3d4257eabf44a666b44c722c9e159dd561bbced94784cceaf8ea"
|
|
12961
12961
|
},
|
|
12962
12962
|
{
|
|
12963
12963
|
"path": "src/module-loader/catalog.js",
|
|
@@ -16097,7 +16097,7 @@
|
|
|
16097
16097
|
{
|
|
16098
16098
|
"path": "tests/instance_feedback.mjs",
|
|
16099
16099
|
"mode": "0000644",
|
|
16100
|
-
"sha256": "
|
|
16100
|
+
"sha256": "a0a558b9d9e0698fa4b6078a2f2e4e2d9679e517149876809c1478743db0f6ff"
|
|
16101
16101
|
},
|
|
16102
16102
|
{
|
|
16103
16103
|
"path": "tests/instance_manifest.mjs",
|
|
@@ -14,7 +14,7 @@ Sends a note about Bongos — the tool — straight to its maintainers (tasks 10
|
|
|
14
14
|
|
|
15
15
|
**When.** File when the builder (a) labels it — "Bongos feedback: …", "tell the Bongos team" — that **is** the go; (b) wishes — "Bongos should…" (**Suggestions count**: send with `--idea`, even if you also fix it in their project); or (c) vents that Bongos is broken — **File it without being asked**, don't argue them out of it. Only about **Bongos itself** (a `bongos`/`/builder-*` command, claim, ship, grade, deploy, upgrade, hall, Settings, sign-in, credits, how it organises work). Not their own project's code, Claude, their laptop or GitHub, nor a rule working as designed. **One filing per distinct problem per session.**
|
|
16
16
|
|
|
17
|
-
**Write it yourself — Never paste the rant.** First line = what went wrong, in at most twelve words (it becomes the title); then what they did, what they expected, what happened, the exact error line. A wish (`--idea`) asks the maintainers to decide something, so it
|
|
17
|
+
**Write it yourself — Never paste the rant.** First line = what went wrong, in at most twelve words (it becomes the title); then what they did, what they expected, what happened, the exact error line. A wish (`--idea`) asks the maintainers to decide something, so open it as an ask (task 1004704): one question of at most twelve words, two or three answers with what each leads to, your pick and why; the rest follows as detail. Write that shape yourself — run no helper first: a project on an older core may not have one, and a failed command before the send is noise (task 1004759). No code, file contents, secrets or personal data.
|
|
18
18
|
|
|
19
19
|
**Send** (stdin, so nothing in the text is mangled by the shell):
|
|
20
20
|
|
|
@@ -24,7 +24,7 @@ bongos feedback --message-file - <<'EOF'
|
|
|
24
24
|
EOF
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Add `--idea` for a wish. The command prints one line — relay it, then carry on. Proactive filing: one short sentence, e.g. *"I've sent that to the Bongos team."* If it says
|
|
27
|
+
Add `--idea` for a wish. The command prints one line — relay it, then carry on. Proactive filing: one short sentence, e.g. *"I've sent that to the Bongos team."* If it says "Not sent", relay that in one line and never retry: "runs only on your computer" and "older Bongos" both mean this project cannot send yet, and the second names the file the note is saved in — send it with `--message-file <that file>` after the project is upgraded. Don't send the builder to GitHub or "the Bongos team" elsewhere instead, and don't call it sent.
|
|
28
28
|
|
|
29
29
|
## Files this skill touches
|
|
30
30
|
|
|
@@ -3133,5 +3133,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
3133
3133
|
landed since 1.21.146 with no explicit bump. run 39274828902. (task 1002620)
|
|
3134
3134
|
1.21.148 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3135
3135
|
landed since 1.21.147 with no explicit bump. run 39286989989. (task 1002620)
|
|
3136
|
+
1.21.149 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
3137
|
+
landed since 1.21.148 with no explicit bump. run 39329687123. (task 1002620)
|
|
3136
3138
|
---------------------------------------------------------------------------
|
|
3137
3139
|
```
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.149",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.149",
|
|
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.21.
|
|
3
|
+
"version": "1.21.149",
|
|
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/release-notes.json
CHANGED
|
@@ -9902,5 +9902,11 @@
|
|
|
9902
9902
|
"id": "1004732",
|
|
9903
9903
|
"text": "When the hub upgrades a project's core, your laptop now notices at the next session start or ship, says so in one line, and on a clean main catches up and reinstalls the new core by itself."
|
|
9904
9904
|
}
|
|
9905
|
+
],
|
|
9906
|
+
"1.21.149": [
|
|
9907
|
+
{
|
|
9908
|
+
"id": "1004759",
|
|
9909
|
+
"text": "When a project's site is too old to send Bongos feedback, the command now says 'Not sent' and keeps the note, instead of claiming it was sent."
|
|
9910
|
+
}
|
|
9905
9911
|
]
|
|
9906
9912
|
}
|
|
@@ -14,8 +14,12 @@
|
|
|
14
14
|
// signed-in builder does nothing extra.
|
|
15
15
|
//
|
|
16
16
|
// Fast and quiet by design (the owner, 2026-10-06): the command prints one line
|
|
17
|
-
// and returns at once; a detached background process
|
|
18
|
-
//
|
|
17
|
+
// and returns at once; a detached background process sends to the hub. The one
|
|
18
|
+
// call made in the foreground is the receipt from the builder's own project, so
|
|
19
|
+
// "Sent" is never printed for a report the project could not vouch for (task
|
|
20
|
+
// 1004759: a project on an older core has no receipt door, and the background
|
|
21
|
+
// 404 was invisible). No task number comes back. Only the typed text
|
|
22
|
+
// (secret-scrubbed) and version stamps travel.
|
|
19
23
|
|
|
20
24
|
'use strict';
|
|
21
25
|
|
|
@@ -47,6 +51,8 @@ const REFUSALS = {
|
|
|
47
51
|
too_long: 'That is too long to send — keep it under 4,000 characters, or split it into two.',
|
|
48
52
|
local_only: 'This project runs only on your computer, so Cloud Bongos cannot confirm who sent the report. Nothing was sent.',
|
|
49
53
|
no_scrubber: 'Feedback was not sent: the secret filter that checks every report is missing from this install.',
|
|
54
|
+
project_too_old: "Not sent: this project's live site runs an older Bongos that cannot send feedback yet. It will work once the project is upgraded.",
|
|
55
|
+
receipt_failed: "Not sent: this project's live site did not confirm who sent it",
|
|
50
56
|
};
|
|
51
57
|
|
|
52
58
|
function stateDir() { return path.join(os.homedir(), '.config', ic.FALLBACK_DIR || 'cloudbongos'); }
|
|
@@ -98,12 +104,15 @@ function repeatKey(message) {
|
|
|
98
104
|
const first = String(message).split('\n').map((l) => l.trim().toLowerCase()).find(Boolean) || '';
|
|
99
105
|
return crypto.createHash('sha256').update(first).digest('hex').slice(0, 16);
|
|
100
106
|
}
|
|
101
|
-
|
|
107
|
+
// `record: false` only looks: the CLI records a report once its project has
|
|
108
|
+
// vouched for it, so a report that never left is not "already sent" (task 1004759).
|
|
109
|
+
function checkAndRecordRepeat(message, { file = path.join(stateDir(), 'feedback-sent.json'), now = Date.now(), record = true } = {}) {
|
|
102
110
|
let seen = {};
|
|
103
111
|
try { seen = JSON.parse(fs.readFileSync(file, 'utf8')) || {}; } catch { /* first report */ }
|
|
104
112
|
for (const [k, t] of Object.entries(seen)) if (!(now - t < REPEAT_WINDOW_MS)) delete seen[k];
|
|
105
113
|
const key = repeatKey(message);
|
|
106
114
|
if (Object.prototype.hasOwnProperty.call(seen, key)) return true;
|
|
115
|
+
if (!record) return false;
|
|
107
116
|
seen[key] = now;
|
|
108
117
|
try {
|
|
109
118
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -112,22 +121,29 @@ function checkAndRecordRepeat(message, { file = path.join(stateDir(), 'feedback-
|
|
|
112
121
|
return false;
|
|
113
122
|
}
|
|
114
123
|
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
|
|
124
|
+
// requestReceipt — the project vouches for this exact text. Foreground: one call
|
|
125
|
+
// to the builder's own project. A 404 means the project's core predates the
|
|
126
|
+
// receipt door (task 1004591), which no retry fixes until it is upgraded.
|
|
127
|
+
async function requestReceipt({ message, kind }, deps = {}) {
|
|
118
128
|
const api = deps.api || await cliClient();
|
|
119
129
|
const base = deps.base || ic.resolveApiBase({ session: loadSessionSync() });
|
|
120
|
-
const doFetch = deps.fetchImpl || fetch;
|
|
121
130
|
const sha256 = crypto.createHash('sha256').update(message, 'utf8').digest('hex');
|
|
122
131
|
const r = await api.request('POST', '/feedback/receipt', { body: { sha256, kind } });
|
|
123
|
-
if (!r.ok) return { ok: false, step: 'receipt', code: errCode(r.data) || `http_${r.status}` };
|
|
132
|
+
if (!r.ok) return { ok: false, step: 'receipt', code: r.status === 404 ? 'project_too_old' : (errCode(r.data) || `http_${r.status}`) };
|
|
124
133
|
const origin = new URL(base).origin;
|
|
125
134
|
const hub = String((r.data && r.data.hub) || origin).replace(/\/+$/, '');
|
|
135
|
+
return { ok: true, receipt: r.data.receipt, origin, hub };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// sendToHub — the vouched report straight to the hub. Returns an outcome
|
|
139
|
+
// object; never prints. Runs in the background child.
|
|
140
|
+
async function sendToHub({ message, kind, receipt, origin, hub }, deps = {}) {
|
|
141
|
+
const doFetch = deps.fetchImpl || fetch;
|
|
126
142
|
const res = await doFetch(`${hub}/api/bongos/sso/feedback/direct`, {
|
|
127
143
|
method: 'POST',
|
|
128
144
|
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
|
|
129
145
|
body: JSON.stringify({
|
|
130
|
-
origin, receipt
|
|
146
|
+
origin, receipt, message, kind,
|
|
131
147
|
context: { cli_version: cliVersion(), os: `${os.platform()} ${os.release()}` },
|
|
132
148
|
}),
|
|
133
149
|
signal: AbortSignal.timeout(15000),
|
|
@@ -138,13 +154,31 @@ async function deliver({ message, kind }, deps = {}) {
|
|
|
138
154
|
return { ok: false, step: 'hub', code: errCode(body) || `http_${res.status}` };
|
|
139
155
|
}
|
|
140
156
|
|
|
157
|
+
// deliver — both halves in one go.
|
|
158
|
+
async function deliver(job, deps = {}) {
|
|
159
|
+
const r = await requestReceipt(job, deps);
|
|
160
|
+
if (!r.ok) return r;
|
|
161
|
+
return sendToHub({ ...job, receipt: r.receipt, origin: r.origin, hub: r.hub }, deps);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// A report the project could not vouch for is kept, so it can be sent after the
|
|
165
|
+
// upgrade (`--message-file <it>`) instead of being written again.
|
|
166
|
+
function keepUnsent(message, { dir = stateDir(), now = new Date() } = {}) {
|
|
167
|
+
try {
|
|
168
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
169
|
+
const file = path.join(dir, `feedback-unsent-${now.toISOString().replace(/[:.]/g, '-')}.md`);
|
|
170
|
+
fs.writeFileSync(file, message, { mode: 0o600 });
|
|
171
|
+
return file;
|
|
172
|
+
} catch { return null; }
|
|
173
|
+
}
|
|
174
|
+
|
|
141
175
|
// The background child: read the queued report, deliver it, record the outcome
|
|
142
176
|
// where a curious builder (or a maintainer helping them) can look.
|
|
143
177
|
async function runDeliverChild(file) {
|
|
144
178
|
let job;
|
|
145
179
|
try { job = JSON.parse(fs.readFileSync(file, 'utf8')); } finally { fs.rmSync(path.dirname(file), { recursive: true, force: true }); }
|
|
146
180
|
let outcome;
|
|
147
|
-
try { outcome = await deliver(job); } catch (e) { outcome = { ok: false, step: 'network', code: String(e && e.message || e).slice(0, 200) }; }
|
|
181
|
+
try { outcome = job.receipt ? await sendToHub(job) : await deliver(job); } catch (e) { outcome = { ok: false, step: 'network', code: String(e && e.message || e).slice(0, 200) }; }
|
|
148
182
|
try {
|
|
149
183
|
fs.writeFileSync(path.join(stateDir(), 'feedback-last.json'), JSON.stringify({ at: new Date().toISOString(), ...outcome }), { mode: 0o600 });
|
|
150
184
|
} catch { /* nowhere to record — the report itself is what mattered */ }
|
|
@@ -177,16 +211,27 @@ async function main(argv = process.argv.slice(2), deps = {}) {
|
|
|
177
211
|
if (isLocalBase(base)) { console.error(REFUSALS.local_only); return 1; }
|
|
178
212
|
|
|
179
213
|
const job = { message: scrubbed.text, kind };
|
|
180
|
-
|
|
214
|
+
const checkRepeat = deps.checkRepeat || checkAndRecordRepeat;
|
|
215
|
+
if (checkRepeat(job.message, { record: false })) {
|
|
181
216
|
console.log('Already sent — the Bongos team has this one.');
|
|
182
217
|
return 0;
|
|
183
218
|
}
|
|
219
|
+
// The receipt in the foreground: "Sent" only once the project has vouched.
|
|
220
|
+
const r = await (deps.requestReceipt || requestReceipt)(job, { base });
|
|
221
|
+
if (!r.ok) {
|
|
222
|
+
const kept = (deps.keepUnsent || keepUnsent)(job.message);
|
|
223
|
+
const why = r.code === 'project_too_old' ? REFUSALS.project_too_old : `${REFUSALS.receipt_failed} (${r.code}).`;
|
|
224
|
+
console.error(kept ? `${why} Your note is saved in ${kept}` : why);
|
|
225
|
+
return 1;
|
|
226
|
+
}
|
|
227
|
+
checkRepeat(job.message);
|
|
228
|
+
const vouched = { ...job, receipt: r.receipt, origin: r.origin, hub: r.hub };
|
|
184
229
|
if (hasFlag('--wait', argv)) {
|
|
185
|
-
const outcome = await (deps.
|
|
230
|
+
const outcome = await (deps.sendToHub || sendToHub)(vouched);
|
|
186
231
|
console.log(outcome.ok ? SENT_LINE : `Not sent (${outcome.step}: ${outcome.code}).`);
|
|
187
232
|
return outcome.ok ? 0 : 1;
|
|
188
233
|
}
|
|
189
|
-
(deps.spawnDelivery || spawnDelivery)(
|
|
234
|
+
(deps.spawnDelivery || spawnDelivery)(vouched);
|
|
190
235
|
console.log(SENT_LINE);
|
|
191
236
|
return 0;
|
|
192
237
|
}
|
|
@@ -201,4 +246,4 @@ if (require.main === module) {
|
|
|
201
246
|
});
|
|
202
247
|
}
|
|
203
248
|
|
|
204
|
-
module.exports = { main, deliver, readMessage, REFUSALS, scrubReport, isLocalBase, repeatKey, checkAndRecordRepeat, SENT_LINE };
|
|
249
|
+
module.exports = { main, deliver, requestReceipt, sendToHub, keepUnsent, readMessage, REFUSALS, scrubReport, isLocalBase, repeatKey, checkAndRecordRepeat, SENT_LINE };
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.149'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -326,6 +326,8 @@ async function runCli(argv, extra = {}) {
|
|
|
326
326
|
requireSession: async () => ({ token: 't' }),
|
|
327
327
|
base: 'https://mercury.cloudbongos.com',
|
|
328
328
|
checkRepeat: () => false,
|
|
329
|
+
requestReceipt: async () => ({ ok: true, receipt: 'R'.repeat(24), origin: 'https://mercury.cloudbongos.com', hub: 'https://cloudbongos.com' }),
|
|
330
|
+
keepUnsent: () => null,
|
|
329
331
|
spawnDelivery: (job) => spawned.push(job),
|
|
330
332
|
...extra,
|
|
331
333
|
});
|
|
@@ -337,7 +339,49 @@ test('the CLI prints one line and hands the network to the background', async ()
|
|
|
337
339
|
const r = await runCli(['claim', 'hung']);
|
|
338
340
|
assert.equal(r.code, 0);
|
|
339
341
|
assert.deepEqual(r.out, ['Sent to the Bongos team.']);
|
|
340
|
-
assert.deepEqual(r.spawned, [{ message: 'claim hung', kind: 'bug' }]);
|
|
342
|
+
assert.deepEqual(r.spawned, [{ message: 'claim hung', kind: 'bug', receipt: 'R'.repeat(24), origin: 'https://mercury.cloudbongos.com', hub: 'https://cloudbongos.com' }]);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// Task 1004759 (toast, core 1.21.20): the project's live site had no receipt
|
|
346
|
+
// door, the background child got a 404, and the builder had been told "Sent".
|
|
347
|
+
test('a project too old to vouch: "Not sent" in the foreground, the note kept, nothing recorded as sent', async () => {
|
|
348
|
+
const kept = []; const recorded = [];
|
|
349
|
+
const r = await runCli(['--idea', 'one', 'skill', 'for', 'project', 'purpose'], {
|
|
350
|
+
requestReceipt: async () => ({ ok: false, step: 'receipt', code: 'project_too_old' }),
|
|
351
|
+
keepUnsent: (m) => { kept.push(m); return '/home/x/.config/cloudbongos/feedback-unsent-1.md'; },
|
|
352
|
+
checkRepeat: (m, o = {}) => { if (o.record !== false) recorded.push(m); return false; },
|
|
353
|
+
});
|
|
354
|
+
assert.equal(r.code, 1);
|
|
355
|
+
assert.deepEqual(r.out, [], 'never says Sent');
|
|
356
|
+
assert.equal(r.spawned.length, 0);
|
|
357
|
+
assert.match(r.err.join('\n'), /older Bongos/);
|
|
358
|
+
assert.match(r.err.join('\n'), /feedback-unsent-1\.md/, 'says where the note is');
|
|
359
|
+
assert.deepEqual(kept, ['one skill for project purpose']);
|
|
360
|
+
assert.deepEqual(recorded, [], 'a report that never left is not "already sent"');
|
|
361
|
+
const other = await runCli(['x'], { requestReceipt: async () => ({ ok: false, step: 'receipt', code: 'not_signed_in' }) });
|
|
362
|
+
assert.equal(other.code, 1);
|
|
363
|
+
assert.match(other.err.join('\n'), /did not confirm.*not_signed_in/);
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
test('the repeat guard records only after the project vouched', async () => {
|
|
367
|
+
const calls = [];
|
|
368
|
+
await runCli(['x'], { checkRepeat: (m, o = {}) => { calls.push(o.record === false ? 'look' : 'record'); return false; } });
|
|
369
|
+
assert.deepEqual(calls, ['look', 'record']);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
test('requestReceipt: a 404 from the project reads as project_too_old', async () => {
|
|
373
|
+
const { requestReceipt } = require_(path.join(ROOT, 'scripts/gds/feedback-send.js'));
|
|
374
|
+
const api = { request: async () => ({ ok: false, status: 404, data: { error: 'Not found' } }) };
|
|
375
|
+
const r = await requestReceipt({ message: 'x', kind: 'idea' }, { api, base: 'https://toast.cloudbongos.com' });
|
|
376
|
+
assert.deepEqual(r, { ok: false, step: 'receipt', code: 'project_too_old' });
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
test('keepUnsent writes the note where the builder can send it again', () => {
|
|
380
|
+
const { keepUnsent } = require_(path.join(ROOT, 'scripts/gds/feedback-send.js'));
|
|
381
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'fb-unsent-'));
|
|
382
|
+
const file = keepUnsent('the note', { dir, now: new Date('2026-10-19T10:00:00Z') });
|
|
383
|
+
assert.equal(fs.readFileSync(file, 'utf8'), 'the note');
|
|
384
|
+
assert.match(path.basename(file), /^feedback-unsent-2026-10-19T10-00-00-000Z\.md$/);
|
|
341
385
|
});
|
|
342
386
|
|
|
343
387
|
test('the CLI refuses a project only this computer can reach, and sends nothing', async () => {
|