@bongos/core 1.19.304 → 1.19.305
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 +16 -11
- package/.claude/skills/ideate/SKILL.md +70 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +3 -1
- package/tests/idea_ideate_full.mjs +170 -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.305",
|
|
6
|
+
"core_contract": "1.19.305",
|
|
7
|
+
"source_commit": "6dc5a84be60b26f11d572eeccf10b8092c7352ae",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-23T19:
|
|
9
|
+
"built_at": "2026-08-23T19:20:49.275Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
12
|
"docs_redacted": 322,
|
|
13
13
|
"agent_docs_stubbed": 22,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1414,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 1758,
|
|
20
|
+
"tree_sha256": "3a7cd2627a967fe3dbe7edf3dc916b4d728170b826669b64ae457d5e92ac10d1",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
{
|
|
178
178
|
"path": ".claude/skills/ideate/SKILL.md",
|
|
179
179
|
"mode": "0000644",
|
|
180
|
-
"sha256": "
|
|
180
|
+
"sha256": "7e2c13e70e3ac208c4754f32685c2453c501f35b3742310ee49a5855bc0c399b"
|
|
181
181
|
},
|
|
182
182
|
{
|
|
183
183
|
"path": ".claude/skills/merge-mode/SKILL.md",
|
|
@@ -4577,12 +4577,12 @@
|
|
|
4577
4577
|
{
|
|
4578
4578
|
"path": "package-lock.json",
|
|
4579
4579
|
"mode": "0000644",
|
|
4580
|
-
"sha256": "
|
|
4580
|
+
"sha256": "4678a5bfd751ffe8ac5c3283eb25af2e011ccb14d32032bf373c7c77bcb61a3d"
|
|
4581
4581
|
},
|
|
4582
4582
|
{
|
|
4583
4583
|
"path": "package.json",
|
|
4584
4584
|
"mode": "0000644",
|
|
4585
|
-
"sha256": "
|
|
4585
|
+
"sha256": "5ff86f97c1df0bf8d224512adc339aac2859db0a65e40e7783032b0128f7cbbd"
|
|
4586
4586
|
},
|
|
4587
4587
|
{
|
|
4588
4588
|
"path": "public-docs/index.html",
|
|
@@ -6237,7 +6237,7 @@
|
|
|
6237
6237
|
{
|
|
6238
6238
|
"path": "src/module-api.js",
|
|
6239
6239
|
"mode": "0000644",
|
|
6240
|
-
"sha256": "
|
|
6240
|
+
"sha256": "a59a54dc09613fc6d95a2efa1262b6d8099fabfb1ef90289462b81accd9c7fea"
|
|
6241
6241
|
},
|
|
6242
6242
|
{
|
|
6243
6243
|
"path": "src/module-loader/catalog.js",
|
|
@@ -7564,6 +7564,11 @@
|
|
|
7564
7564
|
"mode": "0000644",
|
|
7565
7565
|
"sha256": "1e6d0784ecb37e613561cc8ee45fa23e178ecf112d5a8f218b9795dca0e7d0fc"
|
|
7566
7566
|
},
|
|
7567
|
+
{
|
|
7568
|
+
"path": "tests/idea_ideate_full.mjs",
|
|
7569
|
+
"mode": "0000644",
|
|
7570
|
+
"sha256": "5000ed6a7b1a24eced0f070e556af354c8fa9c7937f43c7b932a3ca29f8a5f35"
|
|
7571
|
+
},
|
|
7567
7572
|
{
|
|
7568
7573
|
"path": "tests/idea_quick_guard.mjs",
|
|
7569
7574
|
"mode": "0000644",
|
|
@@ -72,6 +72,76 @@ The body should carry the development, not just a sentence. Use these line-tag c
|
|
|
72
72
|
- The developed block when you have it: `surfaces:`, `risks:`, `open questions:`.
|
|
73
73
|
- In autonomous mode only: `origin: autonomous`.
|
|
74
74
|
|
|
75
|
+
## Authoring a FULL IDEA in conversation (task 1002931 / BV1.R155)
|
|
76
|
+
|
|
77
|
+
A **Full Idea** is the ideator craft's real output: five answered questions that
|
|
78
|
+
are a baseline scope of work someone else can deliver against *without
|
|
79
|
+
re-interviewing the person who had the thought*. Drawing those five answers out
|
|
80
|
+
of a conversation is exactly this skill's dynamic — and it is the path for
|
|
81
|
+
someone who thinks by talking rather than by filling in a form.
|
|
82
|
+
|
|
83
|
+
**Get the questions from the template, never from memory.** They are declared
|
|
84
|
+
once, as data, and a project can define its own:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
node scripts/gds/spark.js --template
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
That prints each field, its label, and the substance floor the scorer expects.
|
|
91
|
+
Ask them **as questions, in that order** — one at a time, in the ideator's own
|
|
92
|
+
thread of thought, not as a form read aloud.
|
|
93
|
+
|
|
94
|
+
### The rule that governs this whole section: you write down what they said
|
|
95
|
+
|
|
96
|
+
You are already "the instrument, not the author". Here that stops being a stance
|
|
97
|
+
and becomes a hard constraint, because **the completeness score is deterministic
|
|
98
|
+
and it feeds credits** (R142 scores substance; ADR 0172 pays on it). If you
|
|
99
|
+
ghost-write a field, the number is fraudulent and the builder is paid for your
|
|
100
|
+
words. So:
|
|
101
|
+
|
|
102
|
+
- **Never invent field content.** Not a sentence, not a clause.
|
|
103
|
+
- **Never pad a thin answer** to clear the floor. A short answer scores short —
|
|
104
|
+
that is the scorer working, not a problem to fix.
|
|
105
|
+
- **Never polish an answer into words they did not use.** Tightening *their*
|
|
106
|
+
phrasing when they ask is help; rewriting it is authorship.
|
|
107
|
+
- **An unanswered question stays empty.** If they do not want to answer "why does
|
|
108
|
+
it matter", file it without that field and let the score say so. A Full Idea
|
|
109
|
+
with a gap is honest; a Full Idea you completed for them is not.
|
|
110
|
+
- What you *should* do is what this skill is for: **press on a thin answer.**
|
|
111
|
+
"That is one line — what is behind it?" is the sounding board doing its job.
|
|
112
|
+
Their next sentence is theirs. Yours would not be.
|
|
113
|
+
|
|
114
|
+
Read the answers back before filing and let them correct you. They bless it; you
|
|
115
|
+
file it.
|
|
116
|
+
|
|
117
|
+
### Filing it
|
|
118
|
+
|
|
119
|
+
Write their answers to a JSON file keyed by the template's field keys, then:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
node scripts/gds/capture.js "<their title>" --full --fields-file <path>
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
That is the **same route** the hall form uses (`POST /inbox` with `grade='full'`),
|
|
126
|
+
so the record is identical — same validation against the R141 template, same R142
|
|
127
|
+
score, same ideator-credit lane. There is deliberately no separate write path for
|
|
128
|
+
conversational authoring: a second one would drift, and "indistinguishable in the
|
|
129
|
+
database" is this task's done-when.
|
|
130
|
+
|
|
131
|
+
The command reports what the server **stored**. If it says the idea landed as
|
|
132
|
+
*quick*, say so plainly — capture is fail-open on grading, and telling someone
|
|
133
|
+
they filed a Full Idea when they did not is the one outcome worse than the
|
|
134
|
+
failure itself.
|
|
135
|
+
|
|
136
|
+
### Developing someone else's spark
|
|
137
|
+
|
|
138
|
+
The same conversation works on a **Big Idea Spark** — a quick idea whose author
|
|
139
|
+
marked it as needing development. `node scripts/gds/spark.js --list` shows what is
|
|
140
|
+
waiting; develop one with `spark.js --develop <id> --fields-file <path>`. The
|
|
141
|
+
author keeps the credit for having had the thought and the payout splits with
|
|
142
|
+
them (ADR 0185), so the never-invent rule applies with more force, not less:
|
|
143
|
+
those five answers are now going out under two people's names.
|
|
144
|
+
|
|
75
145
|
## Trust boundary — filing is open, triage is not
|
|
76
146
|
|
|
77
147
|
Filing into the inbox is open to **any** authenticated builder (Xenos included) — that path stays open here. **Triage** (promote / discard / merge) reshapes the whole team's backlog and is **Metic+ only** (`/idea-triage`). So in this skill you *file*; you never verdict. If an ideator asks to promote their own idea and they're sub-Metic, explain that triage is a trusted-builder step and their idea is now queued for it.
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.305",
|
|
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.305",
|
|
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.305",
|
|
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
|
@@ -1035,8 +1035,10 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
1035
1035
|
// landed since 1.19.302 with no explicit bump. run 32605477232. (task 1002620)
|
|
1036
1036
|
// 1.19.304 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1037
1037
|
// landed since 1.19.303 with no explicit bump. run 32660527575. (task 1002620)
|
|
1038
|
+
// 1.19.305 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1039
|
+
// landed since 1.19.304 with no explicit bump. run 32660902666. (task 1002620)
|
|
1038
1040
|
// ---------------------------------------------------------------------------
|
|
1039
|
-
const CORE_VERSION = '1.19.
|
|
1041
|
+
const CORE_VERSION = '1.19.305'; // CI auto-patch carrier (ADR 0161); see changelog above
|
|
1040
1042
|
|
|
1041
1043
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
1042
1044
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// tests/idea_ideate_full.mjs
|
|
2
|
+
//
|
|
3
|
+
// task 1002931 (BV1.R155, goal 1000062) — /ideate authors a Full Idea
|
|
4
|
+
// conversationally.
|
|
5
|
+
//
|
|
6
|
+
// A skill is a prompt, so the thing to pin is the PROMPT'S CONTRACT. Two
|
|
7
|
+
// done-when clauses, and both are checkable:
|
|
8
|
+
//
|
|
9
|
+
// 1. "indistinguishable in the database from one authored in the hall" — so the
|
|
10
|
+
// skill must file through the SAME route the hall uses, and must not describe
|
|
11
|
+
// a bespoke write path of its own. A second path is how two surfaces start
|
|
12
|
+
// producing subtly different rows.
|
|
13
|
+
// 2. "never invents field content on the builder's behalf" — the sharp one. The
|
|
14
|
+
// completeness score is deterministic and feeds credits (ADR 0172), so a
|
|
15
|
+
// ghost-written field is a fraudulent number and the builder is paid for the
|
|
16
|
+
// model's words. The rule must be present WITH that reason: an instruction
|
|
17
|
+
// whose justification is missing is the first one dropped under pressure.
|
|
18
|
+
//
|
|
19
|
+
// Plus the rule this goal applies to every surface: the five questions live in the
|
|
20
|
+
// R141 declaration, so no consumer restates them.
|
|
21
|
+
//
|
|
22
|
+
// Run: node tests/idea_ideate_full.mjs
|
|
23
|
+
|
|
24
|
+
import { strict as assert } from 'node:assert';
|
|
25
|
+
import { createRequire } from 'node:module';
|
|
26
|
+
import { spawnSync } from 'node:child_process';
|
|
27
|
+
import fs from 'node:fs';
|
|
28
|
+
import path from 'node:path';
|
|
29
|
+
import { fileURLToPath } from 'node:url';
|
|
30
|
+
|
|
31
|
+
const require = createRequire(import.meta.url);
|
|
32
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
33
|
+
const SKILL = fs.readFileSync(path.join(ROOT, '.claude/skills/ideate/SKILL.md'), 'utf8');
|
|
34
|
+
// Prose assertions run against a WHITESPACE-NORMALISED copy. Markdown is hard-
|
|
35
|
+
// wrapped, so any phrase long enough to be worth asserting will eventually
|
|
36
|
+
// straddle a newline — and a regex that does not allow for that fails on a
|
|
37
|
+
// reflow that changed nothing. Structural assertions (code fences, list markers)
|
|
38
|
+
// still use SKILL itself.
|
|
39
|
+
const NORM = SKILL.replace(/\s+/g, ' ');
|
|
40
|
+
const { BASE_TEMPLATE } = require('../modules/ideas/templates.js');
|
|
41
|
+
|
|
42
|
+
let passed = 0;
|
|
43
|
+
let failed = 0;
|
|
44
|
+
function test(name, fn) {
|
|
45
|
+
try { fn(); passed++; console.log(` ok ${name}`); }
|
|
46
|
+
catch (err) { failed++; console.error(` FAIL ${name}\n ${err.message}`); }
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --- (1) the same record as the hall ------------------------------------
|
|
50
|
+
|
|
51
|
+
// THE COMMAND THE SKILL NAMES MUST ACTUALLY EXIST — and this assertion is here
|
|
52
|
+
// because a grader caught its absence. A test that pins prompt text and stops
|
|
53
|
+
// there will happily pass while the skill documents a command that does nothing:
|
|
54
|
+
// R155's branch was cut before R151 added these flags, so for a while the skill
|
|
55
|
+
// said `capture.js --full` to a capture.js that had never heard of it. Prompt
|
|
56
|
+
// tests must reach the executable, or they only prove the prose is self-consistent.
|
|
57
|
+
test('capture.js really supports the flags the skill tells you to use', () => {
|
|
58
|
+
const capture = path.join(ROOT, 'scripts/gds/capture.js');
|
|
59
|
+
const usage = spawnSync(process.execPath, [capture], { encoding: 'utf8', timeout: 20000 });
|
|
60
|
+
assert.equal(usage.status, 2, 'the no-args usage path');
|
|
61
|
+
assert.match(usage.stderr, /--full/, 'usage advertises --full');
|
|
62
|
+
assert.match(usage.stderr, /--fields-file/, 'usage advertises --fields-file');
|
|
63
|
+
|
|
64
|
+
// Stronger than the usage text: --full must be UNDERSTOOD, not merely listed.
|
|
65
|
+
// An unrecognised flag would be silently ignored and file a quick idea; the
|
|
66
|
+
// R151 guard proves the flag is wired to real behaviour.
|
|
67
|
+
const guard = spawnSync(process.execPath, [capture, 'a title', '--full'], { encoding: 'utf8', timeout: 20000 });
|
|
68
|
+
assert.equal(guard.status, 2);
|
|
69
|
+
assert.match(guard.stderr, /--full needs --fields-file/, '--full is parsed and validated');
|
|
70
|
+
|
|
71
|
+
// And it can actually send a grade — the thing that makes the record match the
|
|
72
|
+
// hall's. Asserted over the payload assignment, not the file text.
|
|
73
|
+
const src = fs.readFileSync(capture, 'utf8');
|
|
74
|
+
const grades = [...src.matchAll(/payload\.grade\s*=\s*'([a-z]+)'/g)].map((m) => m[1]);
|
|
75
|
+
assert.deepEqual(grades, ['full'], 'capture.js sends grade=full');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('it files through capture.js --full — the route the hall form uses', () => {
|
|
79
|
+
assert.match(SKILL, /capture\.js "<their title>" --full --fields-file/);
|
|
80
|
+
// Named explicitly so the next reader knows WHY it is that command and not a
|
|
81
|
+
// bespoke one.
|
|
82
|
+
assert.match(NORM, /same.{0,10}route.{0,40}hall form uses/i);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('it describes NO second write path', () => {
|
|
86
|
+
// The failure this guards: a skill that tells an agent to curl the API by hand
|
|
87
|
+
// ends up sending a different payload than the form does, and the two records
|
|
88
|
+
// drift while both claim to be Full Ideas.
|
|
89
|
+
assert.ok(!/curl/i.test(SKILL), 'no hand-rolled HTTP');
|
|
90
|
+
assert.ok(!/api\.js POST \/api\/gds\/inbox/.test(SKILL), 'no raw POST for filing an idea');
|
|
91
|
+
const grade = [...SKILL.matchAll(/grade\s*=\s*'([a-z]+)'/g)].map((m) => m[1]);
|
|
92
|
+
assert.ok(grade.every((g) => g === 'full'), `only the full grade is named: ${JSON.stringify(grade)}`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('it says the report comes from the SERVER, not from the request', () => {
|
|
96
|
+
// Capture is fail-open on grading (R143): an idea can land as quick.
|
|
97
|
+
assert.match(NORM, /reports what the server \*\*stored\*\*/);
|
|
98
|
+
assert.match(NORM, /landed as.{0,20}quick/);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// --- (2) it never invents field content ---------------------------------
|
|
102
|
+
|
|
103
|
+
test('the never-invent rule is stated, in more than one form', () => {
|
|
104
|
+
assert.match(NORM, /Never invent field content/);
|
|
105
|
+
assert.match(NORM, /Never pad a thin answer/);
|
|
106
|
+
assert.match(NORM, /Never polish an answer into words they did not use/);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test('and it carries the REASON — the score feeds credits', () => {
|
|
110
|
+
// An instruction without its justification is the first one dropped when the
|
|
111
|
+
// model is trying to be helpful. This is the sentence that makes ghost-writing
|
|
112
|
+
// legible as fraud rather than as service.
|
|
113
|
+
assert.match(NORM, /deterministic/);
|
|
114
|
+
assert.match(NORM, /feeds credits/);
|
|
115
|
+
assert.match(NORM, /fraudulent/);
|
|
116
|
+
assert.match(NORM, /paid for your words/i);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test('an unanswered question is allowed to stay empty', () => {
|
|
120
|
+
// The tempting failure is completing the set "to be helpful", which is exactly
|
|
121
|
+
// the ghost-writing the rule forbids.
|
|
122
|
+
assert.match(NORM, /stays empty/);
|
|
123
|
+
assert.match(NORM, /honest/);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('it names the thing to do INSTEAD — press on a thin answer', () => {
|
|
127
|
+
// A rule that only forbids leaves the model with nothing to do; this is the
|
|
128
|
+
// sounding-board behaviour the whole skill exists for.
|
|
129
|
+
assert.match(NORM, /press on a thin answer/i);
|
|
130
|
+
assert.match(NORM, /Their next sentence is theirs/);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('the builder blesses it; the skill files it', () => {
|
|
134
|
+
assert.match(NORM, /They bless it; you file it\./);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// --- the five questions are not restated -------------------------------
|
|
138
|
+
|
|
139
|
+
test('the skill names NO field key and NO field label', () => {
|
|
140
|
+
// Same rule the hall composer obeys (R152): the questions are declared once, in
|
|
141
|
+
// the R141 template, and a copy here would silently ignore a per-project one.
|
|
142
|
+
for (const f of BASE_TEMPLATE.fields) {
|
|
143
|
+
assert.ok(!SKILL.includes(f.key), `restates the field key "${f.key}"`);
|
|
144
|
+
assert.ok(!SKILL.includes(f.label), `restates the label "${f.label}"`);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test('it points at the command that PRINTS the questions', () => {
|
|
149
|
+
assert.match(SKILL, /spark\.js --template/);
|
|
150
|
+
assert.match(NORM, /never from memory/i);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// --- the sibling path ---------------------------------------------------
|
|
154
|
+
|
|
155
|
+
test('it can also develop someone else\'s spark, with the rule reinforced', () => {
|
|
156
|
+
assert.match(SKILL, /spark\.js --list/);
|
|
157
|
+
assert.match(SKILL, /spark\.js --develop/);
|
|
158
|
+
// The never-invent rule matters MORE there: the answers go out under two names.
|
|
159
|
+
assert.match(NORM, /two people's names/);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test('the section does not contradict the skill\'s founding rule', () => {
|
|
163
|
+
// "you are the instrument, not the author" is the whole premise; the Full Idea
|
|
164
|
+
// section has to be an application of it, not an exception to it.
|
|
165
|
+
assert.match(NORM, /you are the instrument, not the author/);
|
|
166
|
+
assert.match(NORM, /stops being a stance and becomes a hard constraint/);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
console.log(`\nidea_ideate_full: ${passed} passed, ${failed} failed`);
|
|
170
|
+
if (failed > 0) process.exit(1);
|