@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 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.304",
6
- "core_contract": "1.19.304",
7
- "source_commit": "8c22da0cc4ab95a1684933ef98112abb201bc850",
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:13:32.623Z",
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": 1413,
14
+ "functional_verbatim": 1414,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 1757,
20
- "tree_sha256": "644acfeea1c7e902934e366c56379f0191e6de8e08e6338567155722b916b2fd",
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": "8c89cdb688f39e9ca9009cfb0345f8f75fb12ff91de31914d410c5b7f78d18c2"
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": "3f027a59178132ea747301cbcc78f7404c0f0ec820c6651affcf8854b1cf70f3"
4580
+ "sha256": "4678a5bfd751ffe8ac5c3283eb25af2e011ccb14d32032bf373c7c77bcb61a3d"
4581
4581
  },
4582
4582
  {
4583
4583
  "path": "package.json",
4584
4584
  "mode": "0000644",
4585
- "sha256": "79d94ba55244fa52d3f8f35717803666064daecb4d91d5547060baaa2143692d"
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": "b8db1ca313e0156a5891a779be650f8c56069015529e064a18b8e4970e7e312f"
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.304",
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.304",
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.304",
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.304'; // CI auto-patch carrier (ADR 0161); see changelog above
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);