@bongos/core 1.19.702 → 1.19.704

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.
@@ -92,7 +92,89 @@ const EXPECTED_GLOBS_BEFORE_R101 = [
92
92
  // Each entry is { after, glob, why }: `after` is the glob it is inserted
93
93
  // directly below, so the expected list is reconstructed in order rather than
94
94
  // re-pasted (a re-paste is how the two lists drift apart).
95
- const ADDED_SINCE_R101 = [];
95
+ // CONSIDERED AND DELIBERATELY NOT ADDED: `.claude/agents/` (task 1002492).
96
+ //
97
+ // It looks like it belongs — an agent definition is local automation that fires
98
+ // on its own and declares its own scope, which is the stated reason
99
+ // `.claude/hooks/` and `.claude/settings.json` sit in the automation group. It
100
+ // was added, and backed out, because protectedness is DERIVED PER MODULE:
101
+ // module-scope-map marks a module protected when ANY of its globs is, and
102
+ // `.claude/agents/` is one of the `agents` module's globs. So the entry silently
103
+ // rank-walls the WHOLE agents module — `modules/agents/` source included — and
104
+ // with it goals 1000038/39/40, changing who may work them. Two guards caught it:
105
+ // module-scope-map's "derived, not hand-set" assertion and publish_manifest's
106
+ // rule that every protected glob be explicitly excluded or publishable.
107
+ //
108
+ // That is a policy decision about a rank floor on three goals. It is NOT the
109
+ // decision task 1002492 was for — "an agent may not scope itself onto the
110
+ // authority surface" — which the scope_paths wall in
111
+ // modules/agents/lib/validate.js now enforces on its own. Protecting the
112
+ // directory would gate who may EDIT a definition; it does nothing about where a
113
+ // definition POINTS.
114
+ //
115
+ // If the owner does want the agents module rank-walled, it needs the entry here,
116
+ // a publish-manifest carve-out, and the module-scope-map roster test updated —
117
+ // three deliberate edits, not a side effect of a scope fix.
118
+ const ADDED_SINCE_R101 = [
119
+ // task 1003366, audit ref MIT1 (2026-08-29 security audit, goal 1000084). The EIGHT
120
+ // execution roots the merge resolver runs out of a cloned branch. The audit named four
121
+ // of them ("the three gen-* scripts and .gitattributes"); modules/lifecycle/executed-
122
+ // closure.js names eight, so the audit's prose was already stale against shipped code.
123
+ // The drift tripwire below is what keeps this list honest from here.
124
+ {
125
+ after: 'scripts/gds/cli-lib.js',
126
+ glob: 'scripts/gds/git-merge-regen.js',
127
+ why: 'the otb-regen MERGE DRIVER. Registered as a relative command with cwd at the '
128
+ + 'clone root, so git merge runs the BRANCH copy with the live server environment '
129
+ + 'BEFORE any post-merge step — the earliest execution vector, and the one the '
130
+ + "audit's \"after the merge\" framing missed.",
131
+ },
132
+ {
133
+ after: 'scripts/gds/git-merge-regen.js',
134
+ glob: 'scripts/gds/gen-repo-map.js',
135
+ why: 'a post-merge generator the resolver runs as execFile(node, [<clone>/...]) — the '
136
+ + "branch's own copy, inheriting DATABASE_URL and BUILDER_SECRET_KEY.",
137
+ },
138
+ {
139
+ after: 'scripts/gds/gen-repo-map.js',
140
+ glob: 'scripts/gds/gen-file-map.js',
141
+ why: 'a post-merge generator the resolver executes out of the clone, on the same path '
142
+ + 'as gen-repo-map.js.',
143
+ },
144
+ {
145
+ after: 'scripts/gds/gen-file-map.js',
146
+ glob: 'scripts/gds/gen-session-index.js',
147
+ why: 'a post-merge generator the resolver executes out of the clone, on the same path '
148
+ + 'as gen-repo-map.js.',
149
+ },
150
+ {
151
+ after: 'scripts/gds/gen-session-index.js',
152
+ glob: 'scripts/gds/copy-inventory.js',
153
+ why: 'a post-merge generator the resolver executes out of the clone. NOT one of the '
154
+ + "audit's \"gen-* trio\" — it is the first of the four roots that prose omitted, "
155
+ + 'which is why this registry is pinned to the closure rather than to the audit.',
156
+ },
157
+ {
158
+ after: 'scripts/gds/copy-inventory.js',
159
+ glob: 'scripts/gds/gen-api-docs.js',
160
+ why: 'a post-merge generator the resolver executes out of the clone, and the widest '
161
+ + 'one: it require()s src/bongos/route-rank-check, api-prefix and routes/_helpers, '
162
+ + 'which is why a restore scoped to scripts/gds/ was never sufficient.',
163
+ },
164
+ {
165
+ after: 'scripts/gds/gen-api-docs.js',
166
+ glob: 'scripts/gds/gen-api-client.js',
167
+ why: 'a post-merge generator the resolver executes out of the clone, inheriting the '
168
+ + 'live server environment like its siblings.',
169
+ },
170
+ {
171
+ after: 'scripts/gds/gen-api-client.js',
172
+ glob: '.gitattributes',
173
+ why: 'never require()d, so it is not code — but it NAMES which paths git hands the '
174
+ + 'otb-regen driver, so editing it changes how much of the tree that driver '
175
+ + 'rewrites during a merge. executed-closure.js carries it as TRUSTED_NON_CODE.',
176
+ },
177
+ ];
96
178
 
97
179
  // Surfaces RENAMED since R101 — still protected, under a new path. Distinct from
98
180
  // a removal on purpose: a rename keeps the protection and moves it, a removal
@@ -172,6 +254,55 @@ test('every glob added since R101 carries a documented reason', () => {
172
254
  }
173
255
  });
174
256
 
257
+ // ---------- the drift tripwire: every EXECUTION ROOT is a protected surface ----------
258
+ //
259
+ // task 1003366, audit ref MIT1. The audit prescribed "the three gen-* scripts and
260
+ // .gitattributes" — four surfaces. modules/lifecycle/executed-closure.js (task 1003367,
261
+ // ref B1) names SEVEN execution roots plus .gitattributes, because GENERATOR_SCRIPTS grew
262
+ // after the audit was written. Pasting the audit's four names in would therefore have
263
+ // shipped a hand-curated list that was ALREADY stale on the day it landed, leaving
264
+ // git-merge-regen.js — the earliest vector of the three — writable by any rank.
265
+ //
266
+ // So the registry is pinned to the closure's own ENTRYPOINTS, and this test is what makes
267
+ // the pin real: add a generator to GENERATOR_SCRIPTS (executed_closure.mjs already asserts
268
+ // those two lists are identical) and the build reds HERE until the registry grows to match.
269
+ //
270
+ // WHY THE ENTRYPOINTS AND NOT THE WHOLE 81-FILE CLOSURE — two controls, different jobs:
271
+ // - the closure's TRANSITIVE TAIL is enforced by conflict-resolve.js steps 5b/4b, which
272
+ // REFUSE to auto-resolve a branch touching any of the 81. That refusal is substrate:
273
+ // it does not depend on the builder cooperating, and it cannot drift, because it is
274
+ // computed rather than listed.
275
+ // - this registry is COOPERATIVE (pre-push hook, grader pre-pass, main-audit). Its job
276
+ // is the rank floor and the audit trail. The task says so: MIT1 is the interim
277
+ // mitigation, B1 is the close.
278
+ // Listing all 81 here would add no enforcement the refusal does not already provide, while
279
+ // floor-gating ordinary source at Metic — src/bongos/logger.js, src/branding.js and
280
+ // src/build-info.js are all in the closure — and locking newcomers out of routine work.
281
+ // The entrypoints are different in kind: they exist ONLY to be executed by the pipeline,
282
+ // which is exactly what group 'pipeline' already means for ship.js and gate-review.js.
283
+ test('every execution root in the closure is a protected surface (MIT1 drift tripwire)', () => {
284
+ const ec = require(path.join(ROOT, 'modules', 'lifecycle', 'executed-closure.js'));
285
+ // ENTRYPOINTS lists both script-tree spellings across the C-series rename window; only
286
+ // the ones that exist can be executed, so only those must be protected.
287
+ const roots = [
288
+ ...ec.ENTRYPOINTS.filter((rel) => fs.existsSync(path.join(ROOT, rel))),
289
+ ...ec.TRUSTED_NON_CODE,
290
+ ];
291
+ assert.ok(roots.length >= 8,
292
+ 'fixture sanity: the closure must declare the execution roots this test checks');
293
+ for (const rel of roots) {
294
+ const surface = ppc.surfaceFor(rel);
295
+ assert.ok(surface,
296
+ `${rel} is an EXECUTION ROOT — the merge resolver runs it out of a cloned branch `
297
+ + 'with the live server environment — but no protected surface covers it. Add it to '
298
+ + 'modules/government/protected-surfaces.json (group "pipeline") and declare it in '
299
+ + 'ADDED_SINCE_R101 above.');
300
+ assert.ok(ppc.RANK_TIER[surface.floor] >= ppc.RANK_TIER.metic,
301
+ `${rel} is an execution root floored at '${surface.floor}' — below Metic it stays `
302
+ + 'writable by the ranks ADR 0043 keeps out of the pipeline.');
303
+ }
304
+ });
305
+
175
306
  // Metic is a FLOOR, not a fixed value (task 1003191). Pinning every surface to
176
307
  // exactly 'metic' made the registry's own contract untestable and, worse,
177
308
  // illegal: governance owns this data and raising one surface to 'archon' is a