@bongos/core 1.19.796 → 1.19.797

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.796",
6
- "core_contract": "1.19.796",
7
- "source_commit": "eb84110e187e21a515d265977ce83f905d8ca85a",
5
+ "core_version": "1.19.797",
6
+ "core_contract": "1.19.797",
7
+ "source_commit": "8f5cae915f47995fbf47d3adaeeb356c02f4a9f7",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-17T01:04:56.932Z",
9
+ "built_at": "2026-09-17T01:13:47.184Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 486,
13
13
  "agent_docs_stubbed": 25,
14
- "functional_verbatim": 2182,
14
+ "functional_verbatim": 2183,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2693,
20
- "tree_sha256": "19d44026eb8aaa1ed4e9b87d3ff059e80fe495cbb5ba3e19bf74a36fe69632d0",
19
+ "file_count": 2694,
20
+ "tree_sha256": "c15e5cbc3148cc9a221215b7607ed8cdc79df894902b2e7390bc9baa08d49bc6",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -207,7 +207,7 @@
207
207
  {
208
208
  "path": ".claude/skills/planning-session/SKILL.md",
209
209
  "mode": "0000644",
210
- "sha256": "ede2aa15718bff19d38ffa7f42d64bae7490a9025f7e85d43c43306da3f611b6"
210
+ "sha256": "9320b9a04d36d3169e5a01e4fb5dc595fe84ef292541307fd17974a283b4c509"
211
211
  },
212
212
  {
213
213
  "path": ".claude/skills/priority-session/SKILL.md",
@@ -2527,7 +2527,7 @@
2527
2527
  {
2528
2528
  "path": "docs/module-api-changelog.md",
2529
2529
  "mode": "0000644",
2530
- "sha256": "393caf0055b05621fc26bb45600687b9f02f66d3efcb6774661001d93a6a1847"
2530
+ "sha256": "e0e6a2d6f7b83b899430db2acfa43e9385fd35237cf0245cf54c415be7fe9424"
2531
2531
  },
2532
2532
  {
2533
2533
  "path": "docs/modules-contract.md",
@@ -7702,12 +7702,12 @@
7702
7702
  {
7703
7703
  "path": "package-lock.json",
7704
7704
  "mode": "0000644",
7705
- "sha256": "f93bb0db7f1d64f92d574b1c67095cb6e23f4101da20886678ac869477005630"
7705
+ "sha256": "21954c7eccc561a9ad23abf43f4511040f8b67231aeb91fa88e47d324c3ff18d"
7706
7706
  },
7707
7707
  {
7708
7708
  "path": "package.json",
7709
7709
  "mode": "0000644",
7710
- "sha256": "ddba830385dd1e004134ca3b317969fb69aa5772b607e2d650fae222915ec51e"
7710
+ "sha256": "4a4cd623c012c80ec9a7d18065e78a47209ed2ded81d282e796e9bca6ce7ce6b"
7711
7711
  },
7712
7712
  {
7713
7713
  "path": "public-docs/index.html",
@@ -9587,7 +9587,7 @@
9587
9587
  {
9588
9588
  "path": "src/module-api.js",
9589
9589
  "mode": "0000644",
9590
- "sha256": "a7ef0a9cd6da08076cf17a8de8eae34b588fe51f05f0be618e9fd4e37951e214"
9590
+ "sha256": "12ad8d673d39e15d3ccf283f17927e66dbcf499f1b5243f15bd6cb46545bb1e2"
9591
9591
  },
9592
9592
  {
9593
9593
  "path": "src/module-loader/catalog.js",
@@ -10979,6 +10979,11 @@
10979
10979
  "mode": "0000644",
10980
10980
  "sha256": "3c420d28341a6e5aaeb5d4af978e72acefec3c9bd7d97f66fd11a72517118606"
10981
10981
  },
10982
+ {
10983
+ "path": "tests/goal_unknown_module_hint.mjs",
10984
+ "mode": "0000644",
10985
+ "sha256": "7e7863c75b1282150fb090da90da8628f68a97873c27527978d806fa9d2c4835"
10986
+ },
10982
10987
  {
10983
10988
  "path": "tests/goal_wall_proof.mjs",
10984
10989
  "mode": "0000644",
@@ -134,6 +134,9 @@ For the version being planned, **group the agreed criteria into goals**. For eac
134
134
  - **`scope_modules`** — the module **keys** (NOT file paths) whose files this goal's members may touch. **This is the wall, and it is set here and only here — never self-granted.** Keep it tight: the modules the goal's work actually needs. Widening later goes through an explicit request a trusted rank approves (`POST /goals/:id/scope`; Archon for a `protected` module) — BV1.R64.
135
135
  > **The valid key list is the source of truth in [`src/bongos/module-scope-map.js`](../../../src/bongos/module-scope-map.js) — read it, don't trust this list.** It exports `moduleKeys()` (all keys), `protectedModules()` / `isProtectedModule(key)` (which are PROTECTED), and `scopeForModule(key)` (a key's globs + `protected` flag). Each key maps to path-globs; `protected` is **derived** (the key's globs overlap the trust/ship-pipeline/migration/authz core), and a goal scoped to any protected key is **Archon-only** to create. The current roster (run `node -e "const m=require('./src/bongos/module-scope-map');console.log(m.moduleKeys().map(k=>k+(m.isProtectedModule(k)?' 🔒':'')).join('\n'))"` to print it live):
136
136
  > - **No roster is written down here on purpose.** The previous snapshot went stale within a release (it still omitted a key that had been added), which is exactly the failure a "read it live" rule prevents. Run the one-liner above; `protected` is derived from glob overlap, so it changes when the globs change.
137
+ > - **That file is the SOURCE. The RUNNING SERVER is the GATE.** `POST /goals` validates `scope_modules` against the roster the LIVE server loaded — not the one in your checkout — so a key added since the last deploy is valid here and refused on the wire. Merging to main deploys nothing (gate 3 of [the release pipeline](../../../docs/recipes/core-release-pipeline.md)), and this is not a rare race: the module roster grows precisely during the versions when planning sessions run. On 2026-08-16 goal 1000069 was created with `government` in scope and came back `unknown: ["government"]` — the key was on main; the site was a release behind. **The SessionStart deploy-state line already tells you whether you are exposed.** If it says the site is behind main, treat any recently-added key as suspect before you compose the goal, not after.
138
+ > - **The refusal is not a dead end — it hands you the answer.** `unknown_module` comes back with `valid: [...]`: the LIVE roster, from the server that just refused you. One rejected call tells you exactly what will be accepted, so read it and re-point rather than trying keys one at a time. (`tests/goal_unknown_module_hint.mjs` pins that the refusal keeps carrying it, because this instruction is only true while it does.)
139
+ > - **Prefer a deployed alias where one exists, and write the re-point down.** Goal 1000069 was walled to the deprecated `governance` alias, which ADR 0174 deliberately kept carrying BOTH sets of globs through the transition — so the wall was identical, not weaker — and the goal description recorded that `scope_modules` should be re-pointed once the newer release went live. A goal walled to an alias with no note is a goal nobody re-points.
137
140
  - **No covering key? → file a module-map prep task FIRST.** Every `scope_modules` entry must be a real key from `moduleKeys()`. If a goal's planned work touches file paths that **no existing key covers** (e.g. a brand-new module dir, or code still fused under `src/bongos/` that isn't yet broken out as its own key), the scope wall cannot place that work. Do **not** invent a key. Instead, **add a prep task to this version** — `kind='refactor'`, scoped to `kernel` (the module that owns `module-scope-map.js`) — to extend `MODULE_GLOBS` so a key covers those paths, and make the dependent goal's tasks depend on it. Adding/editing a key is itself a `kernel`-scoped (Archon-gated) change, so flag it for Archon sign-off in this session.
138
141
  - **Join policy** — a normal-scope goal is **open-join** (any builder); a goal whose scope includes a **`protected`** module is **Archon-mediated** (a non-Archon can't self-join; an Archon admits Metic+ members). Creating a protected-scope goal is itself **Archon-only** — so flag those for Archon sign-off in this session.
139
142
  - **Optional seed tasks / lead** — any obvious first tasks, and who leads.
@@ -2066,5 +2066,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
2066
2066
  landed since 1.19.794 with no explicit bump. run 35168408728. (task 1002620)
2067
2067
  1.19.796 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2068
2068
  landed since 1.19.795 with no explicit bump. run 35169093667. (task 1002620)
2069
+ 1.19.797 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2070
+ landed since 1.19.796 with no explicit bump. run 35169694391. (task 1002620)
2069
2071
  ---------------------------------------------------------------------------
2070
2072
  ```
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.796",
3
+ "version": "1.19.797",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.796",
9
+ "version": "1.19.797",
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.796",
3
+ "version": "1.19.797",
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
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
71
71
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
72
72
  // the entry to that file. Look for a version's history there, not here.
73
73
  // ---------------------------------------------------------------------------
74
- const CORE_VERSION = '1.19.796'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.797'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
75
75
 
76
76
  // A namespaced logger so a module's log lines are attributable + consistent.
77
77
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -0,0 +1,103 @@
1
+ // tests/goal_unknown_module_hint.mjs
2
+ //
3
+ // The `unknown_module` refusal must keep handing back the LIVE module roster
4
+ // (task 1003106, goal 1000095, from idea 1000760).
5
+ //
6
+ // WHY THIS EXISTS. `POST /goals` validates `scope_modules` against the roster
7
+ // the RUNNING server loaded, while a planning session reads the roster from its
8
+ // own checkout. Those are different lists whenever the deployed core pin sits
9
+ // behind `main` — which is the normal state, by design, because merging to main
10
+ // deploys nothing (gate 3 of the release pipeline). On 2026-08-16 a session
11
+ // created a goal with `government` in scope and was refused: the key was on
12
+ // main, the site was a release behind.
13
+ //
14
+ // The fix is guidance in `/planning-session` Phase 2.5, and the part of that
15
+ // guidance with teeth is "the refusal is not a dead end — read `valid` off it
16
+ // and re-point". **That instruction is only true while the refusal carries the
17
+ // list.** If someone trims the payload to a bare code, the skill keeps telling
18
+ // agents to read a field that is no longer there, and the failure is silent:
19
+ // the planning session simply guesses again. So the two are pinned together
20
+ // here — the route's payload, the roster it is built from, and the skill text
21
+ // that depends on both.
22
+ //
23
+ // Deliberately NOT a re-test of the schema layer: `tests/goal_routes.mjs`
24
+ // covers the request-validation branches and says in its own words that the
25
+ // unknown-key check is in-handler, which is exactly the gap this fills.
26
+ //
27
+ // Pure: no DB, no network.
28
+ //
29
+ // Run: node tests/goal_unknown_module_hint.mjs
30
+
31
+ import { strict as assert } from 'node:assert';
32
+ import fs from 'node:fs';
33
+ import path from 'node:path';
34
+ import { fileURLToPath } from 'node:url';
35
+ import { createRequire } from 'node:module';
36
+
37
+ const require = createRequire(import.meta.url);
38
+ const scopeMap = require('../src/bongos/module-scope-map.js');
39
+
40
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
41
+ const GOALS_ROUTE = path.join(ROOT, 'modules', 'lifecycle', 'routes', 'goals.js');
42
+ const SKILL = path.join(ROOT, '.claude', 'skills', 'planning-session', 'SKILL.md');
43
+
44
+ let passed = 0;
45
+ let failed = 0;
46
+ function test(name, fn) {
47
+ try {
48
+ fn();
49
+ passed++;
50
+ console.log(` ok ${name}`);
51
+ } catch (err) {
52
+ failed++;
53
+ console.log(` FAIL ${name}`);
54
+ console.log(` ${err.message}`);
55
+ }
56
+ }
57
+
58
+ // ---- the roster the refusal would hand back is real ------------------------
59
+
60
+ test('moduleKeys() returns a real, non-empty roster', () => {
61
+ const keys = scopeMap.moduleKeys();
62
+ assert.ok(Array.isArray(keys), 'moduleKeys() returns an array');
63
+ assert.ok(keys.length >= 10, `expected a substantial roster, got ${keys.length}`);
64
+ assert.equal(new Set(keys).size, keys.length, 'keys are unique');
65
+ for (const k of keys) {
66
+ assert.ok(typeof k === 'string' && k.trim(), `key ${JSON.stringify(k)} is a non-empty string`);
67
+ assert.ok(scopeMap.isModuleKey(k), `isModuleKey agrees about ${k}`);
68
+ }
69
+ // A roster that answered "yes" to everything would make the refusal
70
+ // unreachable and this whole file vacuous.
71
+ assert.equal(scopeMap.isModuleKey('definitely-not-a-module'), false);
72
+ });
73
+
74
+ // ---- the refusal carries it ------------------------------------------------
75
+
76
+ test('every unknown_module refusal hands back valid: moduleKeys()', () => {
77
+ const src = fs.readFileSync(GOALS_ROUTE, 'utf8');
78
+ const sites = [...src.matchAll(/res\.fail\(\s*'unknown_module'[\s\S]{0,200}?\)/g)].map((m) => m[0]);
79
+ // The count IS the vacuity guard: a regex that silently matched nothing would
80
+ // make the loop below pass without asserting anything at all.
81
+ assert.equal(sites.length, 2, `expected 2 unknown_module refusals in goals.js, found ${sites.length}`);
82
+ for (const site of sites) {
83
+ assert.match(
84
+ site, /valid:\s*moduleKeys\(\)/,
85
+ `an unknown_module refusal without valid: moduleKeys() — /planning-session Phase 2.5 tells agents to read that field:\n${site}`
86
+ );
87
+ }
88
+ });
89
+
90
+ // ---- and the skill still tells agents to read it ---------------------------
91
+
92
+ test('Phase 2.5 tells the agent the server is the gate and the refusal carries the answer', () => {
93
+ const skill = fs.readFileSync(SKILL, 'utf8');
94
+ assert.ok(skill.includes('### Phase 2.5'), 'Phase 2.5 still exists');
95
+ assert.match(skill, /RUNNING SERVER is the GATE/,
96
+ 'Phase 2.5 must still say the live server, not the checkout, decides a key');
97
+ assert.match(skill, /`unknown_module` comes back with `valid/,
98
+ 'Phase 2.5 must still point at the field this test pins');
99
+ });
100
+
101
+ console.log('');
102
+ console.log(`unknown_module carries the live roster (task 1003106): ${passed} passed, ${failed} failed`);
103
+ if (failed > 0) process.exit(1);