@bongos/core 1.19.912 → 1.19.914

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,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.19.912",
6
- "core_contract": "1.19.912",
7
- "source_commit": "d9093cd602f014a948cee8ac7ea27f36d4be922b",
5
+ "core_version": "1.19.914",
6
+ "core_contract": "1.19.914",
7
+ "source_commit": "81b80cbc81505afaf295f223d65f33b098524874",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-20T16:20:10.208Z",
9
+ "built_at": "2026-09-20T16:45:54.496Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 504,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 2817,
20
- "tree_sha256": "e4f492bda78fcddcc35a433c3f5c7ca4b3e193298e923da342a7326e182fb695",
20
+ "tree_sha256": "1f2169776959966454ca4ed89eb86b91320d410e2212011cb5abd19639550c6a",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -1742,7 +1742,7 @@
1742
1742
  {
1743
1743
  "path": "docs/adr/0250-strict-versioning-the-version-boundary-is-the-scope-gate.md",
1744
1744
  "mode": "0000644",
1745
- "sha256": "b79190a5eab7109287b60afd56dd4f935c8b2f87c75a6ab9e8b52f5e76f09257"
1745
+ "sha256": "dc2f3cbb649c974f8c13ae057a8fe662c44548a1d75ff4585b094673d5cbe22d"
1746
1746
  },
1747
1747
  {
1748
1748
  "path": "docs/adr/0251-a-suggestion-of-who-to-invite-is-a-recruiting-surface-and-owes-the-opt-out.md",
@@ -2642,7 +2642,7 @@
2642
2642
  {
2643
2643
  "path": "docs/module-api-changelog.md",
2644
2644
  "mode": "0000644",
2645
- "sha256": "1dfb3933a3ce48499941b71978e9f462fe97c49e7d472b71022b654850093e3b"
2645
+ "sha256": "b47e1d07dc8f26cc6a93d8c0af9ced52c80949e0d118c68acb6f6408024b40f2"
2646
2646
  },
2647
2647
  {
2648
2648
  "path": "docs/modules-contract.md",
@@ -4277,7 +4277,7 @@
4277
4277
  {
4278
4278
  "path": "modules/government/catalog.js",
4279
4279
  "mode": "0000644",
4280
- "sha256": "c705435848db169f6a37832b88ae7280193232a3f0f918388a11b05072836af3"
4280
+ "sha256": "65ff329fd00c94ed4571d852079515e173339ee070d5489ce10d5cabe219b20b"
4281
4281
  },
4282
4282
  {
4283
4283
  "path": "modules/government/config.js",
@@ -7797,12 +7797,12 @@
7797
7797
  {
7798
7798
  "path": "package-lock.json",
7799
7799
  "mode": "0000644",
7800
- "sha256": "d424dadd5439bf9883d9e0e44a938ed30a2e5c63f4dd1ed336e6130dbef959e6"
7800
+ "sha256": "7a0bf95c593548a4af11e8cacd4e79ab9b72edc7fff191c5922e0b0e04959393"
7801
7801
  },
7802
7802
  {
7803
7803
  "path": "package.json",
7804
7804
  "mode": "0000644",
7805
- "sha256": "ffdc1c047d136b67d5443336e8c8874b9cf7c13868a908a486470ffdcc780f3c"
7805
+ "sha256": "f0ee3eff2afb0f48e77843723d28397dfd39e06149216d8e41e05cf3d73fd698"
7806
7806
  },
7807
7807
  {
7808
7808
  "path": "public-docs/index.html",
@@ -9612,7 +9612,7 @@
9612
9612
  {
9613
9613
  "path": "src/bongos/module-scope-map.js",
9614
9614
  "mode": "0000644",
9615
- "sha256": "b82d3257c27689e53b3d491251cc46d474c8f4da944e2e8f73242525df878484"
9615
+ "sha256": "cfcc13ad9d554c4f6f0d8bb4df822a1e2a7f589c2bc3c2829b95b64d00f82cd0"
9616
9616
  },
9617
9617
  {
9618
9618
  "path": "src/bongos/module-submissions.js",
@@ -9802,7 +9802,7 @@
9802
9802
  {
9803
9803
  "path": "src/module-api.js",
9804
9804
  "mode": "0000644",
9805
- "sha256": "b0f164fe792662f67fe4184880ea2197ae4b22bec2696fdcb1693778caf44999"
9805
+ "sha256": "6002aa90924229eb6418bed7f42c380a0c3de8f15f77a0381b44270f37b7dad0"
9806
9806
  },
9807
9807
  {
9808
9808
  "path": "src/module-loader/catalog.js",
@@ -11402,7 +11402,7 @@
11402
11402
  {
11403
11403
  "path": "tests/government_catalog.mjs",
11404
11404
  "mode": "0000644",
11405
- "sha256": "1058c2c691a05b4db0365fed68794de8cffa2c4af2c97f4e8c91ba197f67cb6d"
11405
+ "sha256": "ba1c95ca55d8d8e256ba03f2543e44cee4272b9652742343617e35cebefe3ab9"
11406
11406
  },
11407
11407
  {
11408
11408
  "path": "tests/government_charter_library.mjs",
@@ -12302,7 +12302,7 @@
12302
12302
  {
12303
12303
  "path": "tests/module-scope-map.mjs",
12304
12304
  "mode": "0000644",
12305
- "sha256": "fae747eaeea8e9d657de097a72e5f8d244bd2ece6c7e890ff4c9756787721bc5"
12305
+ "sha256": "4d65821ed847d364a80920b29c82cf1ee8ac06b3a8349845902e58e75cf96c7b"
12306
12306
  },
12307
12307
  {
12308
12308
  "path": "tests/module_api.mjs",
@@ -125,6 +125,33 @@ now a disposition over scope, not a piece of authoring.
125
125
 
126
126
  The rest of ADR 0157 stands; this touches exactly one key.
127
127
 
128
+ ### 6a. The `system` flag on that key: asked, and answered NOT NOW (2026-09-17)
129
+
130
+ Raising the floor left a gap that §6 did not close and did not hide. `version.create`
131
+ is `system: false`, and the `system` flag is what permanently bars granting a key to a
132
+ **custom** rank. So the government may still grant `version.create` to a custom
133
+ sub-Archon rank, and for a builder holding that rank D1 is decorative again: refused a
134
+ goal on a building version, they cut a fresh version and put it there — exactly the
135
+ escape hatch D1 exists to close.
136
+
137
+ The question "should `version.create` become `system: true`, joining `version.close`?"
138
+ was raised as task 1003853 and **decided by the owner on 2026-09-17: not now.**
139
+
140
+ The case *for* was real and was heard: create is now a disposition over scope in this
141
+ ADR's own words, its pair `version.close` is already `system: true`, and a gate a custom
142
+ rank can be granted is not a gate. It lost to the cost of the remedy. `system: true` is
143
+ **permanent and instance-wide** — it would remove for good the ability to delegate
144
+ version authoring to any custom rank, which is a real capability an instance might want,
145
+ and no instance has asked for the loophole to be closed. The gap is theoretical until
146
+ someone defines a custom sub-Archon rank *and* grants it this key; at that moment it
147
+ stops being theoretical and this is worth reopening.
148
+
149
+ This subsection records that the question is **answered, not pending**. A future reader
150
+ should not implement the one-word catalog change on the strength of the For argument
151
+ alone — it was already weighed. `tests/government_catalog.mjs` pins `version.create` at
152
+ `system: false` with the archon floor so the flag cannot move without someone meeting
153
+ this decision head-on.
154
+
128
155
  ## 7. Consequences
129
156
 
130
157
  - The live instance is **not** in the state these rules describe, and the rules cannot be enforced on data that violates them. Bringing it over is explicit work under criterion `sv-cutover-proven-live`: fold `CB-V1` into `BONGOS-V1`, disposition the closed goals holding open tasks, rehome the catch-all tasks, and scope `BONGOS-V2` in `planning`. Each invariant lands with a query proving it.
@@ -2298,5 +2298,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
2298
2298
  landed since 1.19.910 with no explicit bump. run 35520751450. (task 1002620)
2299
2299
  1.19.912 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2300
2300
  landed since 1.19.911 with no explicit bump. run 35522352719. (task 1002620)
2301
+ 1.19.913 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2302
+ landed since 1.19.912 with no explicit bump. run 35523207325. (task 1002620)
2303
+ 1.19.914 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2304
+ landed since 1.19.913 with no explicit bump. run 35523700531. (task 1002620)
2301
2305
  ---------------------------------------------------------------------------
2302
2306
  ```
@@ -139,6 +139,19 @@ const PERMISSIONS = [
139
139
  // is a second-order change to what custom ranks may hold — the same reasoning that
140
140
  // keeps `dependency.cross_tier.manage` where it is — and neither ADR 0250 nor this
141
141
  // task decided it. Filed for the owner rather than taken here.
142
+ //
143
+ // ANSWERED 2026-09-17 — NOT NOW. The owner decided in an Archon session that
144
+ // `version.create` STAYS system:false (task 1003853, ADR 0250 §6a). The gap above is
145
+ // real and stands as written; it is simply not worth what closing it costs today.
146
+ // `system:true` is PERMANENT — it would remove, for every instance and for good, the
147
+ // ability to delegate version authoring to a custom rank, and no instance has asked
148
+ // for the loophole to be closed. The gap stays theoretical until someone actually
149
+ // defines a custom sub-Archon rank AND grants it this key.
150
+ //
151
+ // So: do NOT flip this to `true` on the strength of the "a gate a custom rank can be
152
+ // granted is not a gate" argument. That argument was heard and was not the deciding
153
+ // one. The revisit trigger is a concrete one — the first time such a rank exists —
154
+ // and tests/government_catalog.mjs pins the flag so the change cannot land silently.
142
155
  { key: 'version.create', system: false, floor: 'archon', guards: 'POST /versions — create a version + its done-when criteria (ADR 0250 §6 returns it to archon; ADR 0157 had lowered it to metic)' },
143
156
  { key: 'task.confirm.grade_bypass', system: true, floor: 'archon', guards: 'POST /tasks/:id/confirm — ratify past a failed grade (env-relaxable, ADR 0041)' },
144
157
  { key: 'dependency.cross_tier.manage', system: true, floor: 'archon', guards: 'POST|DELETE /dependencies — cross-tier structural edges (ADR 0086)' },
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.912",
3
+ "version": "1.19.914",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.912",
9
+ "version": "1.19.914",
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.912",
3
+ "version": "1.19.914",
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",
@@ -63,6 +63,12 @@ const MODULE_GLOBS = {
63
63
  // red-team FEATURE carved to modules/security/ (BV1.R79 / 02-module-map §5).
64
64
  'src/bongos/routes/security.js',
65
65
  'server.js', 'config/',
66
+
67
+ // ADOPTED (task 1004111): a protected path owned by no module is authorable
68
+ // by NO goal, because MODULE_GLOBS is the authoring territory and the shared
69
+ // floor is only tests/. cli-lib.js is the CLI's own argv/session plumbing —
70
+ // core library surface, so it sits with the rest of the kernel.
71
+ 'scripts/gds/cli-lib.js',
66
72
  ],
67
73
  // --- the build state machine + ship/grade/deploy orchestration + schema ---
68
74
  // Carved into modules/lifecycle/ (BV1.R86 / ADR 0093 §1,§3): the task/claim/
@@ -81,6 +87,12 @@ const MODULE_GLOBS = {
81
87
  'scripts/gds/gate-review.js', 'scripts/gds/push-main.js', 'scripts/gds/ci-grade.js',
82
88
  'scripts/gds/merge-lock.js', 'scripts/gds/version-close.js',
83
89
  'migrations/',
90
+
91
+ // ADOPTED (task 1004111): the deploy half of the ship pipeline lifecycle
92
+ // already owns end to end — migrate.sh runs the migrations it owns, and
93
+ // scripts/deploy/ is what a merge to main triggers.
94
+ 'scripts/migrate.sh',
95
+ 'scripts/deploy/',
84
96
  ],
85
97
  // --- credit-allocation model + ledger + pricing + gamification ---
86
98
  economy: [
@@ -316,6 +328,17 @@ const MODULE_GLOBS = {
316
328
  'modules/dev-box/', 'devbox-app/',
317
329
  'scripts/gds/box.js', 'scripts/gds/box-auth-check.js', 'scripts/gds/box-connect-lib.js',
318
330
  'scripts/gds/smoke-box.sh',
331
+
332
+ // ADOPTED (task 1004111): the box BRING-UP surface. box.js (already here)
333
+ // orchestrates these five provisioning CLIs, and infra/ is the shell-script
334
+ // tree it resolves from the instance root — the comment above has described
335
+ // infra/ as dev-box's since task 1002386 without the glob saying so.
336
+ 'scripts/gds/provision-config.js',
337
+ 'scripts/gds/provision-net.js',
338
+ 'scripts/gds/box-infra.js',
339
+ 'scripts/gds/cf-tunnel.js',
340
+ 'scripts/gds/do-api.js',
341
+ 'infra/',
319
342
  ],
320
343
  // --- the dev-hygiene enforcement surfaces: CI workflows, the agent permission/
321
344
  // hook files, git hooks, root manifests, and the fitness/entropy tooling
@@ -368,6 +391,20 @@ const MODULE_GLOBS = {
368
391
  'scripts/gds/route-shadow-guard.js', 'scripts/gds/routine-schedule.js',
369
392
  'scripts/gds/seam-wiring-guard.js', 'scripts/gds/skill-preflight.js',
370
393
  'scripts/gds/test-path-guard.js', 'scripts/gds/version-literal-guard.js',
394
+
395
+ // ADOPTED (task 1004111): the enforcement surfaces this module already IS.
396
+ // install-git-hooks.js + scripts/gds/hooks/ are the pre-push hook beside the
397
+ // .husky/ it owns; git-merge-regen.js + .gitattributes are the merge-driver
398
+ // PAIR and must not straddle a module boundary; run-unit-tests.js is the CI
399
+ // runner; gen-api-client.js is the sibling of the gen-api-docs.js above.
400
+ // CODEOWNERS is review routing, which is this module's job.
401
+ 'scripts/gds/install-git-hooks.js',
402
+ 'scripts/gds/hooks/',
403
+ 'scripts/gds/git-merge-regen.js',
404
+ '.gitattributes',
405
+ 'scripts/gds/run-unit-tests.js',
406
+ 'scripts/gds/gen-api-client.js',
407
+ 'CODEOWNERS',
371
408
  ],
372
409
  // --- the METHODOLOGY surfaces: the instruction core an agent session actually
373
410
  // reads — the always-loaded root CLAUDE.md, the docs library it indexes, the
@@ -415,6 +452,13 @@ const MODULE_GLOBS = {
415
452
  'docs/',
416
453
  '.claude/skills/', '.claude/rules/',
417
454
  'scripts/gds/start.js', 'scripts/gds/discipline-modes.json',
455
+
456
+ // ADOPTED (task 1004111): the three freshness generators that write the docs
457
+ // this module owns — repo-map, file-map and the session index all emit into
458
+ // docs/ (and CLAUDE.md), so the generator belongs with its output.
459
+ 'scripts/gds/gen-repo-map.js',
460
+ 'scripts/gds/gen-file-map.js',
461
+ 'scripts/gds/gen-session-index.js',
418
462
  ],
419
463
  };
420
464
 
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.912'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.914'; // 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');
@@ -38,6 +38,28 @@ test('system and operational partition the catalog exactly', () => {
38
38
  assert.deepEqual([...sys, ...op].sort(), permissionKeys().slice().sort());
39
39
  });
40
40
 
41
+ // version.create is the one key in this catalog whose `system` flag is a RECORDED
42
+ // DECISION rather than a derivation, so it gets a tripwire of its own (task 1003853).
43
+ //
44
+ // WHY. ADR 0250 §6 raised its floor to archon; §6a asked whether the flag should follow
45
+ // to `system: true` and the owner answered NOT NOW on 2026-09-17 — `system: true` is
46
+ // permanent and would remove for good the ability to delegate version authoring to a
47
+ // custom rank. The For argument is a good one and is written down in the catalog beside
48
+ // the key, which is exactly why a future reader may act on it without noticing it was
49
+ // already weighed. A comment cannot stop that; a red test can.
50
+ test('version.create stays system:false at the archon floor — an ANSWERED question, not an open one', () => {
51
+ const p = byKey('version.create');
52
+ assert.equal(p.floor, 'archon', 'ADR 0250 §6 returned version.create to the archon floor');
53
+ assert.equal(p.system, false,
54
+ 'version.create must stay system:false. This is not an oversight: ADR 0250 §6a records '
55
+ + 'the owner deciding NOT NOW on 2026-09-17 (task 1003853). system:true is permanent and '
56
+ + 'instance-wide — it removes the ability to delegate version authoring to any custom rank. '
57
+ + 'Read §6a before changing this; the revisit trigger is a custom sub-Archon rank actually '
58
+ + 'existing AND being granted this key.');
59
+ // The pair it is often compared to HAS made that trip, which is the whole temptation.
60
+ assert.equal(isSystem('version.close'), true, 'version.close is system:true — create is deliberately not');
61
+ });
62
+
41
63
  test('isGrantable: operational→true, system→false, unknown→false', () => {
42
64
  assert.equal(isGrantable('idea.triage'), true);
43
65
  assert.equal(isGrantable('builder.rank.set'), false); // system
@@ -14,7 +14,7 @@ const { PROTECTED_GLOBS } = require(path.join(ROOT, 'src', 'bongos', 'permission
14
14
  const gsc = require(path.join(ROOT, 'src', 'bongos', 'goal-scope-check.js'));
15
15
  // the OLD protection rule, recomputed in-test so the shrink-only property below
16
16
  // is proved against real globs rather than a copied literal (ADR 0314).
17
- const { touchesOverlap } = require(path.join(ROOT, 'src', 'bongos', 'path-match.js'));
17
+ const { touchesOverlap, matchOne } = require(path.join(ROOT, 'src', 'bongos', 'path-match.js'));
18
18
 
19
19
  test('roster covers the decided ~16 modules + kernel, each with non-empty globs', () => {
20
20
  const keys = scope.moduleKeys();
@@ -550,3 +550,104 @@ test('moduleDriftFiles degrades safely on junk input', () => {
550
550
  assert.deepEqual(scope.moduleDriftFiles('lifecycle', null), []);
551
551
  assert.deepEqual(scope.moduleDriftFiles(null, ['modules/dev-box/app.js']), []);
552
552
  });
553
+
554
+ // EVERY PROTECTED PATH HAS AN OWNING MODULE (task 1004111).
555
+ //
556
+ // MODULE_GLOBS is the territory deciding who may AUTHOR a task touching a path,
557
+ // and the shared floor is only tests/. So a protected path owned by no module is
558
+ // authorable by NO goal at all — scopeSubset returns out_of_scope whatever the
559
+ // goal is scoped to. Nineteen paths were in that state, including infra/,
560
+ // scripts/deploy/, scripts/migrate.sh, CODEOWNERS and the merge-driver pair.
561
+ //
562
+ // This is a TRIPWIRE, not a snapshot: it is computed over PROTECTED_GLOBS, so a
563
+ // path added to the protected roster without an owner reds the build on the spot.
564
+ //
565
+ // THE PROBE TRAP (ADR 0314's own measurement nearly got this wrong in both
566
+ // directions): matchOne(file, touches) takes an ARRAY as its second argument.
567
+ // Passing a joined string iterates its characters and reports everything
568
+ // unowned. The guard test below pins that, so this one cannot rot into a
569
+ // vacuous pass.
570
+ // Ownership is DIRECTIONAL, deliberately. touchesOverlap is symmetric, so a
571
+ // module owning ONE file inside a protected directory would "own" the whole
572
+ // directory and this tripwire would pass on a path no goal can actually author —
573
+ // which is exactly what it caught: kernel owns src/bongos/routes/security.js,
574
+ // and under symmetric overlap that looked like ownership of src/bongos/routes/.
575
+ // matchOne(p, globs) asks the question the wall asks: does some module's
576
+ // territory COVER this path?
577
+ function ownersOf(p) {
578
+ return scope.moduleKeys().filter((k) => matchOne(p, scope.scopeForModule(k).globs) !== null);
579
+ }
580
+
581
+ // The one PROTECTED_GLOBS entry still owned by nobody, exempted BY NAME so the
582
+ // tripwire stays true rather than being weakened to accommodate it.
583
+ // src/bongos/routes/ is a blanket directory: protection covers all 18 files under
584
+ // it, authoring covers 2 (me.js -> builder-settings, security.js -> kernel).
585
+ // Closing it means deciding who owns the other sixteen live route files, which is
586
+ // an ownership call and not a missing glob — filed as task 1004116. Removing this
587
+ // entry is that task's first step.
588
+ const UNOWNED_EXEMPT = ['src/bongos/routes/'];
589
+
590
+ test('every PROTECTED_GLOBS entry is owned by at least one module', () => {
591
+ const unowned = PROTECTED_GLOBS
592
+ .filter((p) => !UNOWNED_EXEMPT.includes(p))
593
+ .filter((p) => ownersOf(p).length === 0);
594
+ assert.deepEqual(
595
+ unowned, [],
596
+ 'these protected paths are authorable by NO goal — give each an owning module in MODULE_GLOBS (task 1004111)',
597
+ );
598
+ });
599
+
600
+ test('the exemption list is not a place to hide a new hole', () => {
601
+ // An exemption that silently stops applying is worse than none: it would sit
602
+ // in the file implying a known gap that no longer exists, and the next unowned
603
+ // path could be parked beside it. So assert the exemption is still REQUIRED.
604
+ for (const p of UNOWNED_EXEMPT) {
605
+ assert.ok(PROTECTED_GLOBS.includes(p), `${p} is exempted but is no longer a protected glob — drop it`);
606
+ assert.equal(
607
+ ownersOf(p).length, 0,
608
+ `${p} now HAS an owning module — delete it from UNOWNED_EXEMPT (task 1004116 is done)`,
609
+ );
610
+ }
611
+ });
612
+
613
+ test('...and the owner is a real module whose goal can actually author the path', () => {
614
+ // Ownership on paper is not ownership: assert through the REAL wall, so a glob
615
+ // that resolves but does not cover cannot pass. Metic is used deliberately —
616
+ // these are protected paths, so a sub-Metic is refused by the path wall and
617
+ // would prove nothing about scope.
618
+ for (const p of PROTECTED_GLOBS.filter((x) => !UNOWNED_EXEMPT.includes(x))) {
619
+ const [owner] = ownersOf(p);
620
+ assert.ok(owner, `${p} has no owning module`);
621
+ assert.equal(
622
+ gsc.scopeSubset([owner], [p]).ok, true,
623
+ `${p} resolves to owner "${owner}" but that goal's territory does not cover it`,
624
+ );
625
+ }
626
+ });
627
+
628
+ test('adopting the unowned paths did NOT change which modules are protected', () => {
629
+ // The property that made assignment safe rather than a re-coupling: every
630
+ // receiving module was ALREADY protected, so no goal became Archon-only that
631
+ // was not before. If this fails, an adoption widened the trust boundary.
632
+ for (const k of ['kernel', 'lifecycle', 'dev-box', 'devsecops', 'methodology']) {
633
+ assert.equal(scope.scopeForModule(k).protected, true, `${k} was already protected and must stay so`);
634
+ }
635
+ // and the ADR 0314 nine are still exactly the nine — PROTECTED_MODULES above
636
+ // is the by-name pin; this states the intent of THIS task against it.
637
+ assert.equal(scope.protectedModules().length, 9, 'adoption must not change the size of the protected set');
638
+ });
639
+
640
+ test('the probe trap: matchOne takes an ARRAY, and a string reports everything unowned', () => {
641
+ // Pinned because it is how this hole was nearly mis-measured twice. If a future
642
+ // edit passes a joined string, the tripwire above would silently "find" the
643
+ // whole roster unowned — or, inverted, find nothing. Prove the array form is
644
+ // the one with the real answer.
645
+ const p = '.gitattributes';
646
+ assert.ok(PROTECTED_GLOBS.includes(p), 'fixture assumption: .gitattributes is protected');
647
+ assert.ok(ownersOf(p).length > 0, 'the ARRAY form finds the real owner');
648
+ assert.ok(touchesOverlap([p], scope.scopeForModule('devsecops').globs), 'and the symmetric form agrees for an exactly-owned path');
649
+ assert.equal(
650
+ matchOne(p, scope.scopeForModule('devsecops').globs.join(',')), null,
651
+ 'a STRING second argument must not be mistaken for a working query',
652
+ );
653
+ });