@starci/eslint-canon-fe 9.5.23 → 9.6.2

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.
Files changed (164) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/docs/button-intent.md +1 -1
  3. package/package.json +1 -1
  4. package/runtime/bundle.json +192 -0
  5. package/runtime/engine/by-code-unit.mjs +6 -6
  6. package/runtime/engine/model-config.mjs +98 -76
  7. package/runtime/engine/orca-config.mjs +22 -17
  8. package/runtime/engine/release-config.mjs +22 -18
  9. package/runtime/engine/removed-vocabulary.mjs +21 -20
  10. package/runtime/engine/resources-config.mjs +42 -35
  11. package/runtime/engine/runtime-root.mjs +11 -13
  12. package/runtime/engine/secrets.mjs +170 -102
  13. package/runtime/engine/sonar-config.mjs +16 -13
  14. package/runtime/engine/temp-root.mjs +3 -9
  15. package/runtime/knowledge/hfs/canon-pins.yaml +10 -10
  16. package/runtime/knowledge/hfs/slots.yaml +2 -2
  17. package/runtime/knowledge/sonar-gate.yaml +1 -1
  18. package/runtime/packages/hfs/templates/app/package-scripts/package.json +1 -1
  19. package/runtime/scripts/api/git/cat-file.mjs +0 -1
  20. package/runtime/scripts/api/git/config-get.mjs +1 -2
  21. package/runtime/scripts/api/git/diff.mjs +0 -1
  22. package/runtime/scripts/api/git/lib.mjs +32 -32
  23. package/runtime/scripts/api/git/ls-files.mjs +0 -1
  24. package/runtime/scripts/api/git/ls-tree.mjs +0 -1
  25. package/runtime/scripts/api/git/merge-base.mjs +2 -3
  26. package/runtime/scripts/api/git/remote.mjs +0 -1
  27. package/runtime/scripts/api/git/rev-parse-query.mjs +0 -1
  28. package/runtime/scripts/api/git/show.mjs +0 -1
  29. package/runtime/scripts/hfs/allows.mjs +37 -38
  30. package/runtime/scripts/hfs/architecture/ast-walks.mjs +180 -163
  31. package/runtime/scripts/hfs/architecture/automatic-gates.mjs +80 -73
  32. package/runtime/scripts/hfs/architecture/backend-app-files.mjs +88 -77
  33. package/runtime/scripts/hfs/architecture/backend.mjs +378 -357
  34. package/runtime/scripts/hfs/architecture/background-unowned.mjs +201 -187
  35. package/runtime/scripts/hfs/architecture/canon-clones.mjs +260 -209
  36. package/runtime/scripts/hfs/architecture/canon-jsx-clones.mjs +149 -116
  37. package/runtime/scripts/hfs/architecture/client-reaches-server.mjs +72 -68
  38. package/runtime/scripts/hfs/architecture/clones.mjs +212 -217
  39. package/runtime/scripts/hfs/architecture/config-unread.mjs +7 -9
  40. package/runtime/scripts/hfs/architecture/config.mjs +302 -268
  41. package/runtime/scripts/hfs/architecture/connection-map.mjs +258 -225
  42. package/runtime/scripts/hfs/architecture/constructor-deps.mjs +80 -71
  43. package/runtime/scripts/hfs/architecture/context-coupling.mjs +82 -77
  44. package/runtime/scripts/hfs/architecture/context-map.mjs +267 -234
  45. package/runtime/scripts/hfs/architecture/context-owner.mjs +73 -61
  46. package/runtime/scripts/hfs/architecture/context-platform-tables.mjs +47 -43
  47. package/runtime/scripts/hfs/architecture/context-transaction.mjs +54 -52
  48. package/runtime/scripts/hfs/architecture/contract-fixture-guard.mjs +113 -104
  49. package/runtime/scripts/hfs/architecture/contracts-readonly.mjs +288 -270
  50. package/runtime/scripts/hfs/architecture/contracts.mjs +408 -393
  51. package/runtime/scripts/hfs/architecture/cross-app-duplicate.mjs +50 -51
  52. package/runtime/scripts/hfs/architecture/dead-exports-bindings.mjs +143 -119
  53. package/runtime/scripts/hfs/architecture/dead-exports.mjs +286 -228
  54. package/runtime/scripts/hfs/architecture/default-deny.mjs +129 -110
  55. package/runtime/scripts/hfs/architecture/doc-language.mjs +24 -19
  56. package/runtime/scripts/hfs/architecture/entity-registry.mjs +20 -22
  57. package/runtime/scripts/hfs/architecture/entrypoint.mjs +47 -45
  58. package/runtime/scripts/hfs/architecture/error-codes.mjs +37 -35
  59. package/runtime/scripts/hfs/architecture/error-masked.mjs +55 -59
  60. package/runtime/scripts/hfs/architecture/fe-slot-allows.mjs +24 -23
  61. package/runtime/scripts/hfs/architecture/feature-shape.mjs +40 -38
  62. package/runtime/scripts/hfs/architecture/framework-pinned.mjs +21 -21
  63. package/runtime/scripts/hfs/architecture/frontend-block-render.mjs +106 -98
  64. package/runtime/scripts/hfs/architecture/frontend-grammar.mjs +153 -150
  65. package/runtime/scripts/hfs/architecture/frontend-routing.mjs +161 -149
  66. package/runtime/scripts/hfs/architecture/frontend-world-render.mjs +343 -305
  67. package/runtime/scripts/hfs/architecture/frontend-world.mjs +340 -294
  68. package/runtime/scripts/hfs/architecture/frontend.mjs +174 -184
  69. package/runtime/scripts/hfs/architecture/function-spec-providers.mjs +124 -106
  70. package/runtime/scripts/hfs/architecture/hfs-graph.mjs +52 -48
  71. package/runtime/scripts/hfs/architecture/hooks-are-hooks.mjs +81 -71
  72. package/runtime/scripts/hfs/architecture/i18n-keys.mjs +155 -141
  73. package/runtime/scripts/hfs/architecture/identity.mjs +141 -125
  74. package/runtime/scripts/hfs/architecture/index.mjs +272 -272
  75. package/runtime/scripts/hfs/architecture/injection-token-exported.mjs +45 -43
  76. package/runtime/scripts/hfs/architecture/jwt-ast.mjs +195 -176
  77. package/runtime/scripts/hfs/architecture/machine-ast.mjs +163 -155
  78. package/runtime/scripts/hfs/architecture/managed-scripts.mjs +19 -20
  79. package/runtime/scripts/hfs/architecture/module-per-transport.mjs +177 -172
  80. package/runtime/scripts/hfs/architecture/next-data-contract.mjs +100 -78
  81. package/runtime/scripts/hfs/architecture/next-data-inference.mjs +170 -154
  82. package/runtime/scripts/hfs/architecture/next-data-key.mjs +311 -275
  83. package/runtime/scripts/hfs/architecture/next-data-symbols.mjs +17 -17
  84. package/runtime/scripts/hfs/architecture/next-data.mjs +351 -334
  85. package/runtime/scripts/hfs/architecture/operation-contract-roots.mjs +39 -37
  86. package/runtime/scripts/hfs/architecture/owners.mjs +97 -98
  87. package/runtime/scripts/hfs/architecture/package-shape.mjs +56 -55
  88. package/runtime/scripts/hfs/architecture/presentation.mjs +138 -130
  89. package/runtime/scripts/hfs/architecture/reachability.mjs +258 -241
  90. package/runtime/scripts/hfs/architecture/register-once.mjs +152 -139
  91. package/runtime/scripts/hfs/architecture/registration.mjs +301 -267
  92. package/runtime/scripts/hfs/architecture/required-files.mjs +172 -167
  93. package/runtime/scripts/hfs/architecture/route-files-thin.mjs +85 -77
  94. package/runtime/scripts/hfs/architecture/schema-owner.mjs +206 -181
  95. package/runtime/scripts/hfs/architecture/server-action.mjs +48 -48
  96. package/runtime/scripts/hfs/architecture/side-boundary.mjs +2 -5
  97. package/runtime/scripts/hfs/architecture/source-names-shape.mjs +261 -259
  98. package/runtime/scripts/hfs/architecture/source-names.mjs +304 -278
  99. package/runtime/scripts/hfs/architecture/sql-keys.mjs +277 -194
  100. package/runtime/scripts/hfs/architecture/sql-owner.mjs +236 -194
  101. package/runtime/scripts/hfs/architecture/sql-returning.mjs +46 -45
  102. package/runtime/scripts/hfs/architecture/sql-token-forms.mjs +86 -74
  103. package/runtime/scripts/hfs/architecture/sql-tokens.mjs +456 -341
  104. package/runtime/scripts/hfs/architecture/staged-consumers.mjs +35 -30
  105. package/runtime/scripts/hfs/architecture/surface.mjs +64 -74
  106. package/runtime/scripts/hfs/architecture/symbols.mjs +209 -216
  107. package/runtime/scripts/hfs/architecture/test-world-files.mjs +165 -155
  108. package/runtime/scripts/hfs/architecture/tiers.mjs +161 -154
  109. package/runtime/scripts/hfs/architecture/transport-owner.mjs +155 -149
  110. package/runtime/scripts/hfs/architecture/type-context.mjs +255 -250
  111. package/runtime/scripts/hfs/architecture/typescript.mjs +358 -318
  112. package/runtime/scripts/hfs/architecture/unit-spec-providers.mjs +110 -92
  113. package/runtime/scripts/hfs/declaration-shape.mjs +140 -103
  114. package/runtime/scripts/hfs/declaration-slots.mjs +64 -61
  115. package/runtime/scripts/hfs/edition-slots.mjs +43 -40
  116. package/runtime/scripts/hfs/linear-text.mjs +20 -20
  117. package/runtime/scripts/hfs/param-names.mjs +6 -11
  118. package/runtime/scripts/hfs/path-findings.mjs +120 -100
  119. package/runtime/scripts/hfs/repo-identity.mjs +55 -42
  120. package/runtime/scripts/hfs/rule-catalog.mjs +353 -257
  121. package/runtime/scripts/hfs/rule-params-shape.mjs +60 -47
  122. package/runtime/scripts/hfs/rules/database-migrations.mjs +428 -388
  123. package/runtime/scripts/hfs/rules/fe-no-tests.mjs +37 -36
  124. package/runtime/scripts/hfs/rules/read.mjs +19 -15
  125. package/runtime/scripts/hfs/rules/secrets.mjs +1 -1
  126. package/runtime/scripts/hfs/rules/spec-placement.mjs +12 -13
  127. package/runtime/scripts/hfs/slot-app-view.mjs +69 -74
  128. package/runtime/scripts/hfs/slot-classify.mjs +42 -38
  129. package/runtime/scripts/hfs/slot-errors.mjs +8 -7
  130. package/runtime/scripts/hfs/slot-imports.mjs +70 -63
  131. package/runtime/scripts/hfs/slot-manifest-shape.mjs +74 -52
  132. package/runtime/scripts/hfs/slot-match.mjs +122 -111
  133. package/runtime/scripts/hfs/slot-path.mjs +0 -2
  134. package/runtime/scripts/hfs/slot-required.mjs +65 -59
  135. package/runtime/scripts/hfs/slot-semantic-problems.mjs +138 -112
  136. package/runtime/scripts/hfs/slot-side-problems.mjs +23 -19
  137. package/runtime/scripts/hfs/slots.mjs +175 -152
  138. package/runtime/scripts/hfs/test-world-slot.mjs +0 -1
  139. package/runtime/scripts/hfs/trailing-slashes.mjs +4 -4
  140. package/runtime/scripts/hfs/typescript-programs.mjs +66 -69
  141. package/runtime/scripts/hfs/view.mjs +38 -40
  142. package/runtime/scripts/lib/dockerfile.mjs +131 -114
  143. package/runtime/scripts/lib/env.mjs +3 -6
  144. package/runtime/scripts/lib/event-contract.mjs +73 -78
  145. package/runtime/scripts/lib/git.mjs +15 -16
  146. package/runtime/scripts/lib/glob.mjs +23 -14
  147. package/runtime/scripts/lib/grammar-lint.mjs +33 -37
  148. package/runtime/scripts/lib/i18n.mjs +32 -35
  149. package/runtime/scripts/lib/in-order.mjs +57 -38
  150. package/runtime/scripts/lib/json-clone.mjs +45 -36
  151. package/runtime/scripts/lib/json.mjs +26 -14
  152. package/runtime/scripts/lib/language.mjs +133 -129
  153. package/runtime/scripts/lib/list.mjs +9 -14
  154. package/runtime/scripts/lib/mutation-fence.mjs +10 -5
  155. package/runtime/scripts/lib/path-key.mjs +53 -38
  156. package/runtime/scripts/lib/read-text.mjs +30 -17
  157. package/runtime/scripts/lib/regex.mjs +1 -2
  158. package/runtime/scripts/lib/same-text.mjs +0 -1
  159. package/runtime/scripts/lib/sops-envelope.mjs +148 -120
  160. package/runtime/scripts/lib/stack-services.mjs +128 -129
  161. package/runtime/scripts/lib/ts-analysis.mjs +28 -26
  162. package/runtime/scripts/lib/ts-ast.mjs +36 -42
  163. package/runtime/scripts/lib/walk.mjs +33 -27
  164. package/runtime/scripts/lib/yaml-cached.mjs +4 -4
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 9.6.2 - 2026-10-31
4
+
5
+ - Changed: the bundled canon pins name @starci/grammar 0.16.2 (the horizontal Stepper connector line; SurfaceListCard takes its depth from the surface context).
6
+
7
+ ## 9.6.1 - 2026-10-30
8
+
9
+ - Changed: the bundled canon pins name @starci/grammar 0.16.1 (accentCorner, Backdrop, the shell filling the viewport).
10
+
11
+ ## 9.6.0 - 2026-10-30
12
+
13
+ - Changed: the bundled runtime copies are now generated from the hfs build (TypeScript emitted as .mjs) per runtime/bundle.json, with a drift check in CI; the copies the package keeps as its own are listed there and stay hand-maintained; the bundled canon pins name hfs 4.12.0, eslint-canon-be 3.9.0, eslint-canon-fe 9.6.0, the Grammar family 0.16.0, jest-preset 2.5.3 and test-world 1.3.3. No rule of this package changed.
14
+
3
15
  ## 9.5.23 - 2026-10-30
4
16
 
5
17
  - Changed: the bundled canon pins name hfs 4.11.22 (app check and lint pass under 20 findings), eslint-canon-be 3.8.21, eslint-canon-fe 9.5.23 and the Grammar family 0.15.34. No rule changed.
@@ -19,4 +19,4 @@ App `Button` and `IconButton` commands declare `intent`. Grammar derives their v
19
19
  </AlertDialog>
20
20
  ```
21
21
 
22
- `starci app codemod button-intent` defaults to a dry run. Use `--write` to apply deterministic mappings, then resolve every case in its JSON and Markdown report from the command's actual consequence. A destructive-looking label is a review signal rather than authority to choose an intent.
22
+ `starci app codemod button-intent` defaults to a dry run. Use `--execute` to apply deterministic mappings, then resolve every case in its JSON and Markdown report from the command's actual consequence. A destructive-looking label is a review signal rather than authority to choose an intent.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@starci/eslint-canon-fe",
3
- "version": "9.5.23",
3
+ "version": "9.6.2",
4
4
  "description": "Universal front-end lint laws for TypeScript and Next.js, independent of the selected Grammar family.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,192 @@
1
+ {
2
+ "generated": [
3
+ "config.example.yaml",
4
+ "engine/by-code-unit.mjs",
5
+ "engine/model-config.mjs",
6
+ "engine/orca-config.mjs",
7
+ "engine/plain-object.mjs",
8
+ "engine/release-config.mjs",
9
+ "engine/removed-vocabulary.mjs",
10
+ "engine/resources-config.mjs",
11
+ "engine/runtime-root.mjs",
12
+ "engine/secrets.mjs",
13
+ "engine/sonar-config.mjs",
14
+ "engine/temp-root.mjs",
15
+ "engine/yaml.mjs",
16
+ "knowledge/grammars/index.yaml",
17
+ "knowledge/hfs/canon-pins.yaml",
18
+ "knowledge/hfs/fe-stack.yaml",
19
+ "knowledge/patterns/fe/folder.yaml",
20
+ "knowledge/sonar-gate.yaml",
21
+ "modules/kernel/removed-vocabulary.yaml",
22
+ "modules/models/registry.yaml",
23
+ "modules/models/runtimes.yaml",
24
+ "modules/models/tiers.yaml",
25
+ "scripts/api/git/cat-file.mjs",
26
+ "scripts/api/git/config-get.mjs",
27
+ "scripts/api/git/diff.mjs",
28
+ "scripts/api/git/lib.mjs",
29
+ "scripts/api/git/ls-files.mjs",
30
+ "scripts/api/git/ls-tree.mjs",
31
+ "scripts/api/git/merge-base.mjs",
32
+ "scripts/api/git/remote.mjs",
33
+ "scripts/api/git/rev-parse-query.mjs",
34
+ "scripts/api/git/show.mjs",
35
+ "scripts/hfs/allows.mjs",
36
+ "scripts/hfs/architecture/ast-walks.mjs",
37
+ "scripts/hfs/architecture/automatic-gates.mjs",
38
+ "scripts/hfs/architecture/backend-app-files.mjs",
39
+ "scripts/hfs/architecture/backend.mjs",
40
+ "scripts/hfs/architecture/background-unowned.mjs",
41
+ "scripts/hfs/architecture/canon-clones.mjs",
42
+ "scripts/hfs/architecture/canon-jsx-clones.mjs",
43
+ "scripts/hfs/architecture/client-reaches-server.mjs",
44
+ "scripts/hfs/architecture/clones.mjs",
45
+ "scripts/hfs/architecture/config-unread.mjs",
46
+ "scripts/hfs/architecture/config.mjs",
47
+ "scripts/hfs/architecture/connection-map.mjs",
48
+ "scripts/hfs/architecture/constructor-deps.mjs",
49
+ "scripts/hfs/architecture/context-coupling.mjs",
50
+ "scripts/hfs/architecture/context-map.mjs",
51
+ "scripts/hfs/architecture/context-owner.mjs",
52
+ "scripts/hfs/architecture/context-platform-tables.mjs",
53
+ "scripts/hfs/architecture/context-transaction.mjs",
54
+ "scripts/hfs/architecture/contract-fixture-guard.mjs",
55
+ "scripts/hfs/architecture/contracts-readonly.mjs",
56
+ "scripts/hfs/architecture/contracts.mjs",
57
+ "scripts/hfs/architecture/cross-app-duplicate.mjs",
58
+ "scripts/hfs/architecture/dead-exports-bindings.mjs",
59
+ "scripts/hfs/architecture/dead-exports.mjs",
60
+ "scripts/hfs/architecture/default-deny.mjs",
61
+ "scripts/hfs/architecture/doc-language.mjs",
62
+ "scripts/hfs/architecture/entity-registry.mjs",
63
+ "scripts/hfs/architecture/entrypoint.mjs",
64
+ "scripts/hfs/architecture/error-codes.mjs",
65
+ "scripts/hfs/architecture/error-masked.mjs",
66
+ "scripts/hfs/architecture/fe-slot-allows.mjs",
67
+ "scripts/hfs/architecture/feature-shape.mjs",
68
+ "scripts/hfs/architecture/framework-pinned.mjs",
69
+ "scripts/hfs/architecture/frontend-block-render.mjs",
70
+ "scripts/hfs/architecture/frontend-grammar.mjs",
71
+ "scripts/hfs/architecture/frontend-routing.mjs",
72
+ "scripts/hfs/architecture/frontend-world-render.mjs",
73
+ "scripts/hfs/architecture/frontend-world.mjs",
74
+ "scripts/hfs/architecture/frontend.mjs",
75
+ "scripts/hfs/architecture/function-spec-providers.mjs",
76
+ "scripts/hfs/architecture/hfs-graph.mjs",
77
+ "scripts/hfs/architecture/hooks-are-hooks.mjs",
78
+ "scripts/hfs/architecture/i18n-keys.mjs",
79
+ "scripts/hfs/architecture/identity.mjs",
80
+ "scripts/hfs/architecture/index.mjs",
81
+ "scripts/hfs/architecture/injection-token-exported.mjs",
82
+ "scripts/hfs/architecture/jwt-ast.mjs",
83
+ "scripts/hfs/architecture/machine-ast.mjs",
84
+ "scripts/hfs/architecture/managed-scripts.mjs",
85
+ "scripts/hfs/architecture/module-per-transport.mjs",
86
+ "scripts/hfs/architecture/next-data-contract.mjs",
87
+ "scripts/hfs/architecture/next-data-inference.mjs",
88
+ "scripts/hfs/architecture/next-data-key.mjs",
89
+ "scripts/hfs/architecture/next-data-symbols.mjs",
90
+ "scripts/hfs/architecture/next-data.mjs",
91
+ "scripts/hfs/architecture/operation-contract-roots.mjs",
92
+ "scripts/hfs/architecture/owners.mjs",
93
+ "scripts/hfs/architecture/package-shape.mjs",
94
+ "scripts/hfs/architecture/presentation.mjs",
95
+ "scripts/hfs/architecture/reachability.mjs",
96
+ "scripts/hfs/architecture/register-once.mjs",
97
+ "scripts/hfs/architecture/registration.mjs",
98
+ "scripts/hfs/architecture/required-files.mjs",
99
+ "scripts/hfs/architecture/route-files-thin.mjs",
100
+ "scripts/hfs/architecture/schema-owner.mjs",
101
+ "scripts/hfs/architecture/server-action.mjs",
102
+ "scripts/hfs/architecture/side-boundary.mjs",
103
+ "scripts/hfs/architecture/source-names-shape.mjs",
104
+ "scripts/hfs/architecture/source-names.mjs",
105
+ "scripts/hfs/architecture/sql-keys.mjs",
106
+ "scripts/hfs/architecture/sql-owner.mjs",
107
+ "scripts/hfs/architecture/sql-returning.mjs",
108
+ "scripts/hfs/architecture/sql-token-forms.mjs",
109
+ "scripts/hfs/architecture/sql-tokens.mjs",
110
+ "scripts/hfs/architecture/staged-consumers.mjs",
111
+ "scripts/hfs/architecture/surface.mjs",
112
+ "scripts/hfs/architecture/symbols.mjs",
113
+ "scripts/hfs/architecture/test-world-files.mjs",
114
+ "scripts/hfs/architecture/tiers.mjs",
115
+ "scripts/hfs/architecture/transport-owner.mjs",
116
+ "scripts/hfs/architecture/type-context.mjs",
117
+ "scripts/hfs/architecture/typescript.mjs",
118
+ "scripts/hfs/architecture/unit-spec-providers.mjs",
119
+ "scripts/hfs/declaration-shape.mjs",
120
+ "scripts/hfs/declaration-slots.mjs",
121
+ "scripts/hfs/edition-slots.mjs",
122
+ "scripts/hfs/linear-text.mjs",
123
+ "scripts/hfs/param-names.mjs",
124
+ "scripts/hfs/path-findings.mjs",
125
+ "scripts/hfs/repo-identity.mjs",
126
+ "scripts/hfs/rule-catalog.mjs",
127
+ "scripts/hfs/rule-params-shape.mjs",
128
+ "scripts/hfs/rules/database-migrations.mjs",
129
+ "scripts/hfs/rules/fe-no-tests.mjs",
130
+ "scripts/hfs/rules/read.mjs",
131
+ "scripts/hfs/rules/spec-placement.mjs",
132
+ "scripts/hfs/slot-app-view.mjs",
133
+ "scripts/hfs/slot-classify.mjs",
134
+ "scripts/hfs/slot-errors.mjs",
135
+ "scripts/hfs/slot-imports.mjs",
136
+ "scripts/hfs/slot-manifest-shape.mjs",
137
+ "scripts/hfs/slot-match.mjs",
138
+ "scripts/hfs/slot-path.mjs",
139
+ "scripts/hfs/slot-required.mjs",
140
+ "scripts/hfs/slot-semantic-problems.mjs",
141
+ "scripts/hfs/slot-side-problems.mjs",
142
+ "scripts/hfs/slots.mjs",
143
+ "scripts/hfs/test-world-slot.mjs",
144
+ "scripts/hfs/trailing-slashes.mjs",
145
+ "scripts/hfs/typescript-programs.mjs",
146
+ "scripts/hfs/view.mjs",
147
+ "scripts/lib/dockerfile.mjs",
148
+ "scripts/lib/env.mjs",
149
+ "scripts/lib/event-contract.mjs",
150
+ "scripts/lib/git.mjs",
151
+ "scripts/lib/glob.mjs",
152
+ "scripts/lib/grammar-lint.mjs",
153
+ "scripts/lib/i18n.mjs",
154
+ "scripts/lib/in-order.mjs",
155
+ "scripts/lib/json-clone.mjs",
156
+ "scripts/lib/json.mjs",
157
+ "scripts/lib/language.mjs",
158
+ "scripts/lib/list.mjs",
159
+ "scripts/lib/mutation-fence.mjs",
160
+ "scripts/lib/path-key.mjs",
161
+ "scripts/lib/read-text.mjs",
162
+ "scripts/lib/regex.mjs",
163
+ "scripts/lib/same-text.mjs",
164
+ "scripts/lib/sops-envelope.mjs",
165
+ "scripts/lib/stack-services.mjs",
166
+ "scripts/lib/ts-analysis.mjs",
167
+ "scripts/lib/ts-ast.mjs",
168
+ "scripts/lib/walk.mjs",
169
+ "scripts/lib/yaml-cached.mjs"
170
+ ],
171
+ "own": [
172
+ "engine/config.mjs",
173
+ "engine/invalid-config.mjs",
174
+ "knowledge/hfs/slots.yaml",
175
+ "packages/hfs/templates/app/package-scripts/package.json",
176
+ "scripts/api/git/recorded-lines.mjs",
177
+ "scripts/hfs/architecture/hfs.mjs",
178
+ "scripts/hfs/manifest-shape.mjs",
179
+ "scripts/hfs/project-graph.mjs",
180
+ "scripts/hfs/project-rule.mjs",
181
+ "scripts/hfs/rules/database-constants.mjs",
182
+ "scripts/hfs/rules/secrets.mjs",
183
+ "scripts/lib/git-hooks-dir.mjs",
184
+ "scripts/lib/jsdoc.mjs",
185
+ "scripts/lib/line-count.mjs",
186
+ "scripts/lib/next-contract.mjs",
187
+ "scripts/lib/package-at.mjs",
188
+ "scripts/lib/secret-patterns.mjs",
189
+ "scripts/lib/sonar-syntax-rules.mjs",
190
+ "scripts/lib/sonar-way.mjs"
191
+ ]
192
+ }
@@ -1,12 +1,12 @@
1
- // by-code-unit.mjs - the comparator of a bare `Array.prototype.sort()`, owned beside the engine files that digest and canonicalize (scripts/lib/list.mjs re-exports it).
2
-
1
+ // by-code-unit.mjs -the comparator of a bare `Array.prototype.sort()`, owned beside the engine files that digest and canonicalize (scripts/lib/list.mjs re-exports it).
3
2
  /**
4
3
  * The comparator of a bare `Array.prototype.sort()`: values compared as strings by UTF-16 code unit. Pass it to `sort` and
5
4
  * `toSorted` so the order is stated, and stays byte-identical to the default order that digests and canonical output depend on.
6
5
  */
7
6
  export const byCodeUnit = (a, b) => {
8
- const left = String(a);
9
- const right = String(b);
10
- if (left < right) return -1;
11
- return left > right ? 1 : 0;
7
+ const left = String(a);
8
+ const right = String(b);
9
+ if (left < right)
10
+ return -1;
11
+ return left > right ? 1 : 0;
12
12
  };
@@ -1,90 +1,112 @@
1
1
  import fs from 'node:fs';
2
- import {fileURLToPath} from 'node:url';
3
- import {parseYaml} from './yaml.mjs';
4
- import {isPlainObject as plain} from './plain-object.mjs';
5
- import {invalid} from './invalid-config.mjs';
6
- import {byCodeUnit} from './by-code-unit.mjs';
7
- import {removedOfKind} from './removed-vocabulary.mjs';
8
-
2
+ import { fileURLToPath } from 'node:url';
3
+ import { parseYaml } from './yaml.mjs';
4
+ import { isPlainObject as plain } from './plain-object.mjs';
5
+ import { invalid } from './invalid-config.mjs';
6
+ import { byCodeUnit } from './by-code-unit.mjs';
7
+ import { removedOfKind } from './removed-vocabulary.mjs';
9
8
  /** The effort vocabulary, ordered weakest to strongest — the only list of it. */
10
- export const EFFORT_LEVELS=new Set(['none','minimal','low','medium','high','xhigh','max','ultra']);
11
- const DIFFICULTIES=['easy','medium','hard','insane'];
12
- export const MODELS_KEYS=['tiers','seats','balance','usage'];
13
- const known=names=>[...names].sort(byCodeUnit).join(', ');
14
-
15
- const hasPath=(root,dotted)=>dotted.split('.').reduce((node,key)=>(plain(node)&&Object.hasOwn(node,key)?node[key]:undefined),root)!==undefined;
16
-
9
+ export const EFFORT_LEVELS = new Set(['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'ultra']);
10
+ const DIFFICULTIES = ['easy', 'medium', 'hard', 'insane'];
11
+ export const MODELS_KEYS = ['tiers', 'seats', 'balance', 'usage'];
12
+ const known = (names) => [...names].sort(byCodeUnit).join(', ');
13
+ const hasPath = (root, dotted) => dotted.split('.').reduce((node, key) => (plain(node) && Object.hasOwn(node, key) ? node[key] : undefined), root) !== undefined;
17
14
  /** Refuse an old-shape config (the removed keys of modules/kernel/removed-vocabulary.yaml), naming every removed key and where its meaning lives now. */
18
- export function refuseRemovedKeys(config){
19
- const found=removedOfKind('config-key').filter(({name})=>hasPath(config,name));
20
- if(!found.length)return;
21
- const removed=found.map(({name,use})=>`${name} is removed (${use})`).join('; ');
22
- throw new Error(`Invalid config.yaml: ${removed}.`);
15
+ export function refuseRemovedKeys(config) {
16
+ const found = removedOfKind('config-key').filter(({ name }) => hasPath(config, name));
17
+ if (!found.length)
18
+ return;
19
+ const removed = found.map(({ name, use }) => `${name} is removed (${use})`).join('; ');
20
+ throw new Error(`Invalid config.yaml: ${removed}.`);
23
21
  }
24
-
25
- let shippedCache=null;
22
+ let shippedCache = null;
26
23
  /** modules/models/tiers.yaml as shipped, parsed once per file version. */
27
- export function shippedTiers(){
28
- const file=fileURLToPath(new URL('../modules/models/tiers.yaml',import.meta.url));
29
- let stat;try{stat=fs.statSync(file);}catch{throw new Error('Missing modules/models/tiers.yaml');}
30
- const version=`${stat.mtimeMs}:${stat.size}`;
31
- if(shippedCache?.version!==version)shippedCache={version,doc:parseYaml(fs.readFileSync(file,'utf8'))};
32
- return structuredClone(shippedCache.doc);
24
+ export function shippedTiers() {
25
+ const file = fileURLToPath(new URL('../modules/models/tiers.yaml', import.meta.url));
26
+ let stat;
27
+ try {
28
+ stat = fs.statSync(file);
29
+ }
30
+ catch {
31
+ throw new Error('Missing modules/models/tiers.yaml');
32
+ }
33
+ const version = `${stat.mtimeMs}:${stat.size}`;
34
+ if (shippedCache?.version !== version)
35
+ shippedCache = { version, doc: parseYaml(fs.readFileSync(file, 'utf8')) };
36
+ return structuredClone(shippedCache.doc);
33
37
  }
34
-
35
- function validateMember(bad,where,member,profile){
36
- if(!plain(member)||Object.keys(member).some(key=>!['agent','model','effort'].includes(key))||typeof member.agent!=='string'||typeof member.model!=='string')
37
- bad(`${where} must be {agent, model, effort?}.`);
38
- if(profile.models?.[member.model]?.provider!==member.agent)bad(`${where}: model ${member.model} is not declared by agent ${member.agent} in modules/models/registry.yaml models.`);
39
- if(member.effort!==undefined&&!EFFORT_LEVELS.has(member.effort))bad(`${where}.effort must use the effort vocabulary.`);
38
+ function validateMember(bad, where, member, profile) {
39
+ if (!plain(member) || Object.keys(member).some(key => !['agent', 'model', 'effort'].includes(key)) || typeof member.agent !== 'string' || typeof member.model !== 'string')
40
+ return bad(`${where} must be {agent, model, effort?}.`);
41
+ if (profile.models?.[member.model]?.provider !== member.agent)
42
+ bad(`${where}: model ${member.model} is not declared by agent ${member.agent} in modules/models/registry.yaml models.`);
43
+ if (member.effort !== undefined && !(typeof member.effort === 'string' && EFFORT_LEVELS.has(member.effort)))
44
+ bad(`${where}.effort must use the effort vocabulary.`);
40
45
  }
41
-
42
- function validateChains(bad,tiers,profile){
43
- for(const [name,chain] of Object.entries(tiers)){
44
- if(!Array.isArray(chain)||!chain.length)bad(`.${name} must be a non-empty ordered list of {agent, model, effort?}.`);
45
- chain.forEach((member,index)=>validateMember(bad,`.${name}[${index}]`,member,profile));
46
- if(new Set(chain.map(member=>`${member.agent}/${member.model}`)).size!==chain.length)bad(`.${name} names each member once.`);
47
- }
46
+ function validateChains(bad, tiers, profile) {
47
+ for (const [name, chain] of Object.entries(tiers)) {
48
+ if (!Array.isArray(chain) || !chain.length)
49
+ return bad(`.${name} must be a non-empty ordered list of {agent, model, effort?}.`);
50
+ chain.forEach((member, index) => validateMember(bad, `.${name}[${index}]`, member, profile));
51
+ // validateMember refused every member that is not a {agent, model, effort?} record above.
52
+ const members = chain;
53
+ if (new Set(members.map(member => `${member.agent}/${member.model}`)).size !== chain.length)
54
+ bad(`.${name} names each member once.`);
55
+ }
48
56
  }
49
-
50
57
  /** A call tier is taken by a headless call only: no seat, difficulty, kind, order or caller reference may name it, and every call names one. */
51
- function validateUse(bad,{doc,names,refs}){
52
- const callTiers=new Set(Object.entries(doc.tierUse).filter(([,use])=>use==='call').map(([tier])=>tier));
53
- for(const tier of Object.keys(doc.tierUse))if(!names.has(tier))bad(`: tierUse names tier ${tier} (known: ${known(names)}).`);
54
- for(const [where,tier] of refs)if(callTiers.has(tier))bad(`: ${where} names tier ${tier}, a call tier (tierUse): a headless call is made on it, no seat or op is seated on it.`);
55
- for(const [call,spec] of Object.entries(doc.calls))if(!callTiers.has(spec.tier))bad(`: calls.${call}.tier ${spec.tier} must be a tier whose tierUse is call.`);
58
+ function validateUse(bad, { doc, names, refs }) {
59
+ const callTiers = new Set(Object.entries(doc.tierUse).filter(([, use]) => use === 'call').map(([tier]) => tier));
60
+ for (const tier of Object.keys(doc.tierUse))
61
+ if (!names.has(tier))
62
+ bad(`: tierUse names tier ${tier} (known: ${known(names)}).`);
63
+ for (const [where, tier] of refs)
64
+ if (callTiers.has(tier))
65
+ bad(`: ${where} names tier ${tier}, a call tier (tierUse): a headless call is made on it, no seat or op is seated on it.`);
66
+ for (const [call, spec] of Object.entries(doc.calls))
67
+ if (!callTiers.has(spec.tier))
68
+ bad(`: calls.${call}.tier ${spec.tier} must be a tier whose tierUse is call.`);
56
69
  }
57
-
58
70
  /** The tier document the picker reads: the shipped tiers.yaml with config.yaml `models` laid over it. Validated. */
59
- export function effectiveTiers(config,profile){
60
- const doc=shippedTiers(),models=plain(config?.models)?config.models:{};
61
- const bad=invalid('models');
62
- const tiers={...doc.tiers,...models.tiers};
63
- validateChains(bad,tiers,profile);
64
- const seats={...doc.seats,...models.seats};
65
- const balance={...doc.balance,...models.balance};
66
- const usage={...doc.usage,...models.usage};
67
- const names=new Set(Object.keys(tiers));
68
- const refs=[...Object.entries(seats).map(([seat,tier])=>[`.seats.${seat}`,tier]),...DIFFICULTIES.map(level=>[`difficulty.${level}`,doc.difficulty[level]]),
69
- ...Object.entries(doc.kindTiers).map(([kind,tier])=>[`kindTiers.${kind}`,tier]),...doc.tierOrder.map(tier=>['tierOrder',tier])];
70
- for(const [where,tier] of refs)if(!names.has(tier))bad(`: ${where} names tier ${tier} (known: ${known(names)}).`);
71
- validateUse(bad,{doc,names,refs});
72
- if(!(Number.isInteger(balance.maxStreak)&&balance.maxStreak>=1))bad('.balance.maxStreak must be an integer >= 1.');
73
- if(!(typeof balance.maxSharePercent==='number'&&balance.maxSharePercent>0&&balance.maxSharePercent<=100))bad('.balance.maxSharePercent must be a number in (0, 100].');
74
- const {reservePercent,biasPercent,exhaustedPercent}=usage;
75
- if(![reservePercent,biasPercent,exhaustedPercent].every(value=>typeof value==='number'&&value>=0&&value<=100)||!(reservePercent<=biasPercent&&biasPercent<exhaustedPercent))
76
- bad('.usage must hold numbers with reservePercent <= biasPercent < exhaustedPercent, each within 0..100.');
77
- return {...doc,tiers,seats,balance,usage};
71
+ export function effectiveTiers(config, profile) {
72
+ const doc = shippedTiers(), models = plain(config?.models) ? config.models : {};
73
+ const bad = invalid('models');
74
+ const merged = { ...doc.tiers, ...models.tiers };
75
+ validateChains(bad, merged, profile);
76
+ // validateChains refused every chain that is not a non-empty list of {agent, model, effort?} members.
77
+ const tiers = merged;
78
+ const seats = { ...doc.seats, ...models.seats };
79
+ const balance = { ...doc.balance, ...models.balance };
80
+ const usage = { ...doc.usage, ...models.usage };
81
+ const names = new Set(Object.keys(tiers));
82
+ const refs = [...Object.entries(seats).map(([seat, tier]) => [`.seats.${seat}`, tier]), ...DIFFICULTIES.map(level => [`difficulty.${level}`, doc.difficulty[level]]),
83
+ ...Object.entries(doc.kindTiers).map(([kind, tier]) => [`kindTiers.${kind}`, tier]), ...doc.tierOrder.map(tier => ['tierOrder', tier])];
84
+ for (const [where, tier] of refs)
85
+ if (!names.has(tier))
86
+ bad(`: ${where} names tier ${tier} (known: ${known(names)}).`);
87
+ validateUse(bad, { doc, names, refs });
88
+ if (!(Number.isInteger(balance.maxStreak) && balance.maxStreak >= 1))
89
+ bad('.balance.maxStreak must be an integer >= 1.');
90
+ if (!(typeof balance.maxSharePercent === 'number' && balance.maxSharePercent > 0 && balance.maxSharePercent <= 100))
91
+ bad('.balance.maxSharePercent must be a number in (0, 100].');
92
+ const { reservePercent, biasPercent, exhaustedPercent } = usage;
93
+ if (![reservePercent, biasPercent, exhaustedPercent].every(value => typeof value === 'number' && value >= 0 && value <= 100) || !(reservePercent <= biasPercent && biasPercent < exhaustedPercent))
94
+ bad('.usage must hold numbers with reservePercent <= biasPercent < exhaustedPercent, each within 0..100.');
95
+ return { ...doc, tiers, seats, balance, usage };
78
96
  }
79
-
80
97
  /** config.yaml `models` — {tiers?, seats?, balance?, usage?}; the shipped defaults make every key optional. */
81
- export function validateModelsBlock(config,profile){
82
- refuseRemovedKeys(config);
83
- const models=config?.models;
84
- if(models===undefined||models===null)return;
85
- const bad=invalid('models');
86
- if(!plain(models)||Object.keys(models).some(key=>!MODELS_KEYS.includes(key)))bad(` must be {${MODELS_KEYS.map(key=>key+'?').join(', ')}} or null (unknown keys are refused).`);
87
- if(models.tiers!==undefined&&!plain(models.tiers))bad('.tiers must map tier names to ordered member lists.');
88
- for(const key of ['seats','balance','usage'])if(models[key]!==undefined&&!plain(models[key]))bad(`.${key} must be a mapping.`);
89
- effectiveTiers(config,profile);
98
+ export function validateModelsBlock(config, profile) {
99
+ refuseRemovedKeys(config);
100
+ const models = config?.models;
101
+ if (models === undefined || models === null)
102
+ return;
103
+ const bad = invalid('models');
104
+ if (!plain(models) || Object.keys(models).some(key => !MODELS_KEYS.includes(key)))
105
+ return bad(` must be {${MODELS_KEYS.map(key => key + '?').join(', ')}} or null (unknown keys are refused).`);
106
+ if (models.tiers !== undefined && !plain(models.tiers))
107
+ bad('.tiers must map tier names to ordered member lists.');
108
+ for (const key of ['seats', 'balance', 'usage'])
109
+ if (models[key] !== undefined && !plain(models[key]))
110
+ bad(`.${key} must be a mapping.`);
111
+ effectiveTiers(config, profile);
90
112
  }
@@ -1,6 +1,5 @@
1
- import {isPlainObject as plain} from './plain-object.mjs';
2
- import {invalid} from './invalid-config.mjs';
3
-
1
+ import { isPlainObject as plain } from './plain-object.mjs';
2
+ import { invalid } from './invalid-config.mjs';
4
3
  /**
5
4
  * config.yaml `orca` — the owner's Orca app settings the runtime must know. maxWorkerDepth is the deepest worker Orca
6
5
  * lets start under the owner's chat (chat = depth 0, a Kernel or the [Supervisor] 1, an op or a [Worker] 2, the draw
@@ -9,21 +8,27 @@ import {invalid} from './invalid-config.mjs';
9
8
  * deeper than it before worker-start (worker-depth-exceeded), and `start --check` with STARCI_ORCA_LIVE=1 compares it with
10
9
  * a measured probe (scripts/agent/depth-probe.mjs).
11
10
  */
12
- export const ORCA_DEFAULTS=Object.freeze({maxWorkerDepth:4});
11
+ export const ORCA_DEFAULTS = Object.freeze({ maxWorkerDepth: 4 });
13
12
  /** The keys of the orca block. */
14
- export const ORCA_KEYS=Object.freeze(Object.keys(ORCA_DEFAULTS));
15
- export const MAX_WORKER_DEPTH_CEILING=16;
16
- export function validateOrca(orca){
17
- if(orca===null)return;
18
- const bad=invalid('orca');
19
- if(!plain(orca))bad(' must be {maxWorkerDepth?} or null.');
20
- for(const key of Object.keys(orca))if(!ORCA_KEYS.includes(key))bad(` has unknown key ${key} (allowed: ${ORCA_KEYS.join(', ')}).`);
21
- const v=orca.maxWorkerDepth;
22
- if(v!==undefined&&v!==null&&!(Number.isInteger(v)&&v>=1&&v<=MAX_WORKER_DEPTH_CEILING))bad(`.maxWorkerDepth must be an integer from 1 to ${MAX_WORKER_DEPTH_CEILING} equal to the Orca app's worker depth setting (default ${ORCA_DEFAULTS.maxWorkerDepth}), or null.`);
13
+ export const ORCA_KEYS = Object.freeze(Object.keys(ORCA_DEFAULTS));
14
+ export const MAX_WORKER_DEPTH_CEILING = 16;
15
+ export function validateOrca(orca) {
16
+ if (orca === null)
17
+ return;
18
+ const bad = invalid('orca');
19
+ if (!plain(orca))
20
+ return bad(' must be {maxWorkerDepth?} or null.');
21
+ for (const key of Object.keys(orca))
22
+ if (!ORCA_KEYS.includes(key))
23
+ bad(` has unknown key ${key} (allowed: ${ORCA_KEYS.join(', ')}).`);
24
+ const v = orca.maxWorkerDepth;
25
+ if (v !== undefined && v !== null && !(typeof v === 'number' && Number.isInteger(v) && v >= 1 && v <= MAX_WORKER_DEPTH_CEILING))
26
+ bad(`.maxWorkerDepth must be an integer from 1 to ${MAX_WORKER_DEPTH_CEILING} equal to the Orca app's worker depth setting (default ${ORCA_DEFAULTS.maxWorkerDepth}), or null.`);
23
27
  }
24
28
  /** The owner's Orca settings: {maxWorkerDepth, source: 'orca'|'default'}. An absent or null block or key is the default. */
25
- export function orcaSettings(config){
26
- if(config?.orca!==undefined)validateOrca(config.orca);
27
- const value=plain(config?.orca)?config.orca.maxWorkerDepth:null;
28
- return Number.isInteger(value)?{maxWorkerDepth:value,source:'orca'}:{maxWorkerDepth:ORCA_DEFAULTS.maxWorkerDepth,source:'default'};
29
+ export function orcaSettings(config) {
30
+ if (config?.orca !== undefined)
31
+ validateOrca(config.orca);
32
+ const value = plain(config?.orca) ? config.orca.maxWorkerDepth : null;
33
+ return typeof value === 'number' && Number.isInteger(value) ? { maxWorkerDepth: value, source: 'orca' } : { maxWorkerDepth: ORCA_DEFAULTS.maxWorkerDepth, source: 'default' };
29
34
  }
@@ -1,28 +1,32 @@
1
- import {isPlainObject as plain} from './plain-object.mjs';
2
- import {invalid} from './invalid-config.mjs';
3
-
1
+ import { isPlainObject as plain } from './plain-object.mjs';
2
+ import { invalid } from './invalid-config.mjs';
4
3
  /** The keys of the release block. */
5
- export const RELEASE_KEYS=Object.freeze(['suite']);
4
+ export const RELEASE_KEYS = Object.freeze(['suite']);
6
5
  /** Where the full test suite of a release is judged: `local` (the release cut also runs the L4 rows before it tags, the shipped default) or `ci` (the GitHub workflows judge EVERY row on each push to main; the cut runs none). */
7
- export const SUITE_MODES=Object.freeze(['local','ci']);
8
- const RELEASE_DEFAULTS=Object.freeze({suite:'local'});
9
-
6
+ export const SUITE_MODES = Object.freeze(['local', 'ci']);
7
+ const RELEASE_DEFAULTS = Object.freeze({ suite: 'local' });
10
8
  /**
11
9
  * config.yaml `release` - {suite?: local | ci}: who judges the full suite of a release (docs/releasing.md "Where the suite runs"). `local` is the shipped default: `starci release cut` also runs the
12
10
  * L4 rows on this host before it tags. `ci` is the owner's recorded choice (2026-10-09, widened 2026-10-26 by push-main-after-scoped-check): the full suite runs on GitHub only, on every push
13
11
  * to main; the cut runs no row and the verdict is read with `starci release ci-status`. The push of main needs neither mode: it needs a green scoped check.
14
12
  * `none` is refused: the suite does not vanish, it moves to CI, and the value says so.
15
13
  */
16
- export function validateRelease(release){
17
- if(release===null)return;
18
- const bad=invalid('release');
19
- if(!plain(release))bad(` must be {suite?: ${SUITE_MODES.join(' | ')}} or null.`);
20
- for(const key of Object.keys(release))if(!RELEASE_KEYS.includes(key))bad(` has unknown key ${key} (allowed: ${RELEASE_KEYS.join(', ')}).`);
21
- const value=release.suite;
22
- if(value===undefined||value===null)return;
23
- if(value==='none')bad('.suite: "none" is not a mode: write "ci" (no local full suite, CI judges) or "local".');
24
- if(!SUITE_MODES.includes(value))bad(`.suite must be ${SUITE_MODES.join(' or ')}, or null.`);
14
+ export function validateRelease(release) {
15
+ if (release === null)
16
+ return;
17
+ const bad = invalid('release');
18
+ if (!plain(release))
19
+ return bad(` must be {suite?: ${SUITE_MODES.join(' | ')}} or null.`);
20
+ for (const key of Object.keys(release))
21
+ if (!RELEASE_KEYS.includes(key))
22
+ bad(` has unknown key ${key} (allowed: ${RELEASE_KEYS.join(', ')}).`);
23
+ const value = release.suite;
24
+ if (value === undefined || value === null)
25
+ return;
26
+ if (value === 'none')
27
+ bad('.suite: "none" is not a mode: write "ci" (no local full suite, CI judges) or "local".');
28
+ if (typeof value !== 'string' || !SUITE_MODES.includes(value))
29
+ bad(`.suite must be ${SUITE_MODES.join(' or ')}, or null.`);
25
30
  }
26
-
27
31
  /** The suite mode a validated owner config names; an absent block or key is the shipped default. */
28
- export const releaseSuiteMode=config=>config?.release?.suite??RELEASE_DEFAULTS.suite;
32
+ export const releaseSuiteMode = (config) => config?.release?.suite ?? RELEASE_DEFAULTS.suite;
@@ -1,26 +1,27 @@
1
1
  import fs from 'node:fs';
2
- import {fileURLToPath} from 'node:url';
3
- import {parseYaml} from './yaml.mjs';
4
-
5
- const FILE=fileURLToPath(new URL('../modules/kernel/removed-vocabulary.yaml',import.meta.url));
6
-
7
- let cache=null;
8
- function loaded(){
9
- let stat;try{stat=fs.statSync(FILE);}catch{throw new Error('Missing modules/kernel/removed-vocabulary.yaml');}
10
- const version=`${stat.mtimeMs}:${stat.size}`;
11
- if(cache?.version!==version)cache={version,doc:parseYaml(fs.readFileSync(FILE,'utf8'))};
12
- return cache.doc;
2
+ import { fileURLToPath } from 'node:url';
3
+ import { parseYaml } from './yaml.mjs';
4
+ const FILE = fileURLToPath(new URL('../modules/kernel/removed-vocabulary.yaml', import.meta.url));
5
+ let cache = null;
6
+ function loaded() {
7
+ let stat;
8
+ try {
9
+ stat = fs.statSync(FILE);
10
+ }
11
+ catch {
12
+ throw new Error('Missing modules/kernel/removed-vocabulary.yaml');
13
+ }
14
+ const version = `${stat.mtimeMs}:${stat.size}`;
15
+ if (cache?.version !== version)
16
+ cache = { version, doc: parseYaml(fs.readFileSync(FILE, 'utf8')) };
17
+ return cache.doc;
13
18
  }
14
-
15
-
16
19
  /** modules/kernel/removed-vocabulary.yaml: the one list of spellings the runtime refuses, each with its replacement and the release that removed it. */
17
- export const removedVocabulary=()=>loaded().removed;
18
-
20
+ export const removedVocabulary = () => loaded().removed;
19
21
  /** The removed spellings of one kind. */
20
- export const removedOfKind=kind=>removedVocabulary().filter(entry=>entry.kind===kind);
21
-
22
+ export const removedOfKind = (kind) => removedVocabulary().filter(entry => entry.kind === kind);
22
23
  /** `<name> is removed (<replacement>)` for a removed spelling of the kind, or undefined when the name is not one. */
23
- export function removedNotice(kind,name){
24
- const entry=removedOfKind(kind).find(candidate=>candidate.name===name);
25
- return entry?`${entry.name} is removed (${entry.use})`:undefined;
24
+ export function removedNotice(kind, name) {
25
+ const entry = removedOfKind(kind).find(candidate => candidate.name === name);
26
+ return entry ? `${entry.name} is removed (${entry.use})` : undefined;
26
27
  }