@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 +14 -14
- package/docs/adr/0250-strict-versioning-the-version-boundary-is-the-scope-gate.md +27 -0
- package/docs/module-api-changelog.md +4 -0
- package/modules/government/catalog.js +13 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/bongos/module-scope-map.js +44 -0
- package/src/module-api.js +1 -1
- package/tests/government_catalog.mjs +22 -0
- package/tests/module-scope-map.mjs +102 -1
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.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
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:
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
7800
|
+
"sha256": "7a0bf95c593548a4af11e8cacd4e79ab9b72edc7fff191c5922e0b0e04959393"
|
|
7801
7801
|
},
|
|
7802
7802
|
{
|
|
7803
7803
|
"path": "package.json",
|
|
7804
7804
|
"mode": "0000644",
|
|
7805
|
-
"sha256": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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": "
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
+
});
|