@tech-leads-club/harness-toolkit 0.5.0 → 0.6.0
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/bin/generate-schema.ts +67 -0
- package/bin/tlc-build.mjs +26 -1
- package/config.example.json +1 -0
- package/dist/compact-before.mjs +78 -78
- package/dist/doctor.mjs +79 -79
- package/dist/init-project.mjs +87 -87
- package/dist/install-runtime.mjs +82 -82
- package/dist/lessons-cli.mjs +86 -86
- package/dist/obs-cli.mjs +78 -78
- package/dist/prompt-submit.mjs +78 -78
- package/dist/refresh-model-prices.mjs +82 -82
- package/dist/response-after.mjs +78 -78
- package/dist/run.mjs +78 -78
- package/dist/session-end.mjs +83 -83
- package/dist/session-start.mjs +85 -85
- package/dist/shim.mjs +81 -81
- package/dist/stop.mjs +84 -84
- package/dist/subagent-start.mjs +78 -78
- package/dist/subagent-stop.mjs +79 -79
- package/dist/support.mjs +82 -82
- package/dist/tlc-cli.mjs +100 -100
- package/dist/tool-after.mjs +77 -77
- package/dist/tool-before.mjs +78 -78
- package/dist/tool-failure.mjs +78 -78
- package/dist/uninstall-runtime.mjs +3 -3
- package/package.json +4 -2
- package/schema.json +545 -0
- package/src/core/capability/capability.store.ts +27 -1
- package/src/core/comment-policy/comment-policy.service.ts +9 -0
- package/src/core/comment-policy/comment-syntax.catalog.ts +7 -2
- package/src/core/comment-policy/comment-syntax.store.ts +9 -0
- package/src/core/core.facade.ts +7 -1
- package/src/core/policy/policy.loader.ts +5 -1
- package/src/core/policy/policy.shadow.ts +68 -2
- package/src/entrypoints/stop.ts +16 -5
- package/tools/doctor.ts +48 -0
- package/tools/init-project.ts +19 -1
package/schema.json
ADDED
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
{
|
|
2
|
+
"additionalProperties": false,
|
|
3
|
+
"type": "object",
|
|
4
|
+
"properties": {
|
|
5
|
+
"$schema": {
|
|
6
|
+
"type": "string"
|
|
7
|
+
},
|
|
8
|
+
"version": {
|
|
9
|
+
"const": 1,
|
|
10
|
+
"type": "number"
|
|
11
|
+
},
|
|
12
|
+
"mode": {
|
|
13
|
+
"enum": [
|
|
14
|
+
"focus",
|
|
15
|
+
"paired",
|
|
16
|
+
"solo"
|
|
17
|
+
],
|
|
18
|
+
"type": "string"
|
|
19
|
+
},
|
|
20
|
+
"projectName": {
|
|
21
|
+
"type": "string"
|
|
22
|
+
},
|
|
23
|
+
"codePaths": {
|
|
24
|
+
"type": "array",
|
|
25
|
+
"items": {
|
|
26
|
+
"type": "string"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"grind": {
|
|
30
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3BmaxLoops%3Anumber%3BlintCommand%3Astring%5B%5D%7Cnull%3BtestCommand%3Astring%5B%5D%7Cnull%3BappendFiles%3Aimport(%22.%2Fsrc%2Fcore%2Fgate%2Fgate.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).AppendFilesMode%3B%7D%3E"
|
|
31
|
+
},
|
|
32
|
+
"shipGate": {
|
|
33
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3BruntimePathPrefixes%3Astring%5B%5D%3BruntimePathExcludes%3Astring%5B%5D%3BevidenceDir%3Astring%7Cnull%3BevidenceMaxAgeHours%3Anumber%3BemptyDiffAntiShip%3Aboolean%3BclaimWindowMinutes%3Anumber%3B%7D%3E"
|
|
34
|
+
},
|
|
35
|
+
"subagents": {
|
|
36
|
+
"$ref": "#/definitions/Partial%3C%7BenforceAllowlist%3Aboolean%3BrequireModel%3Aboolean%3BallowedModels%3Aimport(%22.%2Fsrc%2Fcore%2Fpolicy%2Fpolicy.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).ProviderScoped%3Cstring%3E%3BblockedPatterns%3Aimport(%22.%2Fsrc%2Fcore%2Fpolicy%2Fpolicy.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).ProviderScoped%3Cstring%3E%3BminEffort%3A%22low%22%7C%22medium%22%7C%22high%22%7C%22xhigh%22%7C%22max%22%7Cnull%3BblockParentFast%3Aboolean%3BblockMode%3A%22deny%22%7C%22ask%22%3BreadOnlyTypes%3Astring%5B%5D%3B%7D%3E"
|
|
37
|
+
},
|
|
38
|
+
"docs": {
|
|
39
|
+
"$ref": "#/definitions/Partial%3C%7Bcommand%3Astring%5B%5D%7Cnull%3Bseverity%3A%22deny%22%7C%22warn%22%3B%7D%3E"
|
|
40
|
+
},
|
|
41
|
+
"observe": {
|
|
42
|
+
"description": "why: observation runs a rail's checker while the rail is not enforcing, so the harness can tell \"the model\nalready does this\" from \"the rule works\". Opt-in and default empty: it costs a diff scan per turn and answers a\nquestion only an operator who is asking it needs answered.",
|
|
43
|
+
"type": "object",
|
|
44
|
+
"properties": {
|
|
45
|
+
"enabled": {
|
|
46
|
+
"type": "boolean"
|
|
47
|
+
},
|
|
48
|
+
"rails": {
|
|
49
|
+
"description": "Rails to observe by name. An unknown name is inert rather than an error — a rail may not exist yet.",
|
|
50
|
+
"type": "array",
|
|
51
|
+
"items": {
|
|
52
|
+
"type": "string"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"required": [
|
|
58
|
+
"enabled",
|
|
59
|
+
"rails"
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
"comments": {
|
|
63
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3BonViolation%3A%22followup%22%7C%22off%22%3Bmode%3Aimport(%22.%2Fsrc%2Fcore%2Fpolicy%2Fpolicy.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).CommentMode%3B%7D%3E"
|
|
64
|
+
},
|
|
65
|
+
"supplyChain": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"properties": {
|
|
68
|
+
"enabled": {
|
|
69
|
+
"type": "boolean"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"additionalProperties": false,
|
|
73
|
+
"required": [
|
|
74
|
+
"enabled"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"duplication": {
|
|
78
|
+
"type": "object",
|
|
79
|
+
"properties": {
|
|
80
|
+
"enabled": {
|
|
81
|
+
"type": "boolean"
|
|
82
|
+
},
|
|
83
|
+
"minRun": {
|
|
84
|
+
"description": "why: the window is the only knob, because it is the only one calibration moved. Six is where matches stopped being punctuation.",
|
|
85
|
+
"type": "number"
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
"additionalProperties": false,
|
|
89
|
+
"required": [
|
|
90
|
+
"enabled",
|
|
91
|
+
"minRun"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
"obs": {
|
|
95
|
+
"$ref": "#/definitions/Partial%3C%7BglobalSpool%3Aboolean%3BincludePayloads%3Aboolean%3BmaxAttrChars%3Anumber%3BsessionCostAlertUsd%3Anumber%7Cnull%3BretentionDays%3Anumber%3B%7D%3E"
|
|
96
|
+
},
|
|
97
|
+
"untrustedContent": {
|
|
98
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3Bmode%3Aimport(%22.%2Fsrc%2Fcore%2Funtrusted%2Funtrusted.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).UntrustedMode%3BextraTools%3Astring%5B%5D%3BextraCommandPatterns%3Astring%5B%5D%3B%7D%3E"
|
|
99
|
+
},
|
|
100
|
+
"planGate": {
|
|
101
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3BwindowMinutes%3Anumber%3B%7D%3E"
|
|
102
|
+
},
|
|
103
|
+
"rules": {
|
|
104
|
+
"$ref": "#/definitions/Partial%3C%7Benabled%3Aboolean%3B%7D%3E"
|
|
105
|
+
},
|
|
106
|
+
"shell": {
|
|
107
|
+
"$ref": "#/definitions/Partial%3C%7BcatastrophicAsk%3Aboolean%3BstallDetection%3Aboolean%3BstallRepeatThreshold%3Anumber%3B%7D%3E"
|
|
108
|
+
},
|
|
109
|
+
"intelligence": {
|
|
110
|
+
"additionalProperties": false,
|
|
111
|
+
"type": "object",
|
|
112
|
+
"properties": {
|
|
113
|
+
"gapFeedback": {
|
|
114
|
+
"type": "boolean"
|
|
115
|
+
},
|
|
116
|
+
"failureClassification": {
|
|
117
|
+
"type": "boolean"
|
|
118
|
+
},
|
|
119
|
+
"progressiveHandoff": {
|
|
120
|
+
"type": "boolean"
|
|
121
|
+
},
|
|
122
|
+
"progressiveContext": {
|
|
123
|
+
"type": "boolean"
|
|
124
|
+
},
|
|
125
|
+
"autopilot": {
|
|
126
|
+
"type": "boolean"
|
|
127
|
+
},
|
|
128
|
+
"idleTurnGate": {
|
|
129
|
+
"type": "boolean"
|
|
130
|
+
},
|
|
131
|
+
"budgetContinue": {
|
|
132
|
+
"type": "boolean"
|
|
133
|
+
},
|
|
134
|
+
"budgetContinueAfterLoops": {
|
|
135
|
+
"type": "number"
|
|
136
|
+
},
|
|
137
|
+
"lessons": {
|
|
138
|
+
"$ref": "#/definitions/Partial%3Cimport(%22.%2Fsrc%2Fcore%2Fpolicy%2Fpolicy.types%22%2C%7Bwith%3A%7B%22resolution-mode%22%3A%22import%22%7D%7D).LessonsPolicyConfig%3E"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
"mcpPrime": {
|
|
143
|
+
"type": "array",
|
|
144
|
+
"items": {
|
|
145
|
+
"type": "string"
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"bootstrapExtra": {
|
|
149
|
+
"type": "array",
|
|
150
|
+
"items": {
|
|
151
|
+
"type": "string"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
"definitions": {
|
|
156
|
+
"AppendFilesMode": {
|
|
157
|
+
"enum": [
|
|
158
|
+
"always",
|
|
159
|
+
"auto",
|
|
160
|
+
"never"
|
|
161
|
+
],
|
|
162
|
+
"type": "string"
|
|
163
|
+
},
|
|
164
|
+
"import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).ProviderScoped<string>": {
|
|
165
|
+
"anyOf": [
|
|
166
|
+
{
|
|
167
|
+
"type": "array",
|
|
168
|
+
"items": {
|
|
169
|
+
"type": "string"
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
"$ref": "#/definitions/Record%3Cstring%2Cstring%5B%5D%3E"
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
},
|
|
177
|
+
"Record<string,string[]>": {
|
|
178
|
+
"type": "object",
|
|
179
|
+
"additionalProperties": false
|
|
180
|
+
},
|
|
181
|
+
"CommentMode": {
|
|
182
|
+
"description": "`declared` requires a reason marker, `strict` accepts no added comment at all, and `resolvable` is `declared`\nplus the question neither can ask: does the comment mean anything to a reader who was not in the session that\nwrote it ([/decisions/ad-070.md](/decisions/ad-070.md))?",
|
|
183
|
+
"enum": [
|
|
184
|
+
"declared",
|
|
185
|
+
"resolvable",
|
|
186
|
+
"strict"
|
|
187
|
+
],
|
|
188
|
+
"type": "string"
|
|
189
|
+
},
|
|
190
|
+
"UntrustedMode": {
|
|
191
|
+
"description": "`frame` states once per turn that outside content is data. `enforce` adds the question framing cannot ask —\ndid this command come from that content — and answers it verbatim\n([/decisions/ad-077.md](/decisions/ad-077.md)).",
|
|
192
|
+
"enum": [
|
|
193
|
+
"enforce",
|
|
194
|
+
"frame"
|
|
195
|
+
],
|
|
196
|
+
"type": "string"
|
|
197
|
+
},
|
|
198
|
+
"LessonsSyncMode": {
|
|
199
|
+
"description": "How the durable provider view is decided. `auto` asks the host: a provider that does not deliver context from\nits session-start hook has no other route, so the view is written there and not where the hook works\n([/decisions/ad-050.md](/decisions/ad-050.md)).",
|
|
200
|
+
"enum": [
|
|
201
|
+
"always",
|
|
202
|
+
"auto",
|
|
203
|
+
"never"
|
|
204
|
+
],
|
|
205
|
+
"type": "string"
|
|
206
|
+
},
|
|
207
|
+
"Partial<{enabled:boolean;maxLoops:number;lintCommand:string[]|null;testCommand:string[]|null;appendFiles:import(\"./src/core/gate/gate.types\",{with:{\"resolution-mode\":\"import\"}}).AppendFilesMode;}>": {
|
|
208
|
+
"type": "object",
|
|
209
|
+
"properties": {
|
|
210
|
+
"enabled": {
|
|
211
|
+
"type": "boolean"
|
|
212
|
+
},
|
|
213
|
+
"maxLoops": {
|
|
214
|
+
"type": "number"
|
|
215
|
+
},
|
|
216
|
+
"lintCommand": {
|
|
217
|
+
"anyOf": [
|
|
218
|
+
{
|
|
219
|
+
"type": "array",
|
|
220
|
+
"items": {
|
|
221
|
+
"type": "string"
|
|
222
|
+
}
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"type": "null"
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
"testCommand": {
|
|
230
|
+
"anyOf": [
|
|
231
|
+
{
|
|
232
|
+
"type": "array",
|
|
233
|
+
"items": {
|
|
234
|
+
"type": "string"
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"type": "null"
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
},
|
|
242
|
+
"appendFiles": {
|
|
243
|
+
"enum": [
|
|
244
|
+
"always",
|
|
245
|
+
"auto",
|
|
246
|
+
"never"
|
|
247
|
+
],
|
|
248
|
+
"type": "string"
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
"additionalProperties": false
|
|
252
|
+
},
|
|
253
|
+
"Partial<{enabled:boolean;runtimePathPrefixes:string[];runtimePathExcludes:string[];evidenceDir:string|null;evidenceMaxAgeHours:number;emptyDiffAntiShip:boolean;claimWindowMinutes:number;}>": {
|
|
254
|
+
"type": "object",
|
|
255
|
+
"properties": {
|
|
256
|
+
"enabled": {
|
|
257
|
+
"type": "boolean"
|
|
258
|
+
},
|
|
259
|
+
"runtimePathPrefixes": {
|
|
260
|
+
"type": "array",
|
|
261
|
+
"items": {
|
|
262
|
+
"type": "string"
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
"runtimePathExcludes": {
|
|
266
|
+
"type": "array",
|
|
267
|
+
"items": {
|
|
268
|
+
"type": "string"
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
"evidenceDir": {
|
|
272
|
+
"type": [
|
|
273
|
+
"null",
|
|
274
|
+
"string"
|
|
275
|
+
]
|
|
276
|
+
},
|
|
277
|
+
"evidenceMaxAgeHours": {
|
|
278
|
+
"type": "number"
|
|
279
|
+
},
|
|
280
|
+
"emptyDiffAntiShip": {
|
|
281
|
+
"type": "boolean"
|
|
282
|
+
},
|
|
283
|
+
"claimWindowMinutes": {
|
|
284
|
+
"type": "number"
|
|
285
|
+
}
|
|
286
|
+
},
|
|
287
|
+
"additionalProperties": false
|
|
288
|
+
},
|
|
289
|
+
"Partial<{enforceAllowlist:boolean;requireModel:boolean;allowedModels:import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).ProviderScoped<string>;blockedPatterns:import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).ProviderScoped<string>;minEffort:\"low\"|\"medium\"|\"high\"|\"xhigh\"|\"max\"|null;blockParentFast:boolean;blockMode:\"deny\"|\"ask\";readOnlyTypes:string[];}>": {
|
|
290
|
+
"type": "object",
|
|
291
|
+
"properties": {
|
|
292
|
+
"enforceAllowlist": {
|
|
293
|
+
"type": "boolean"
|
|
294
|
+
},
|
|
295
|
+
"requireModel": {
|
|
296
|
+
"type": "boolean"
|
|
297
|
+
},
|
|
298
|
+
"allowedModels": {
|
|
299
|
+
"anyOf": [
|
|
300
|
+
{
|
|
301
|
+
"type": "array",
|
|
302
|
+
"items": {
|
|
303
|
+
"type": "string"
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"$ref": "#/definitions/Record%3Cstring%2Cstring%5B%5D%3E"
|
|
308
|
+
}
|
|
309
|
+
]
|
|
310
|
+
},
|
|
311
|
+
"blockedPatterns": {
|
|
312
|
+
"anyOf": [
|
|
313
|
+
{
|
|
314
|
+
"type": "array",
|
|
315
|
+
"items": {
|
|
316
|
+
"type": "string"
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
{
|
|
320
|
+
"$ref": "#/definitions/Record%3Cstring%2Cstring%5B%5D%3E"
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
},
|
|
324
|
+
"minEffort": {
|
|
325
|
+
"anyOf": [
|
|
326
|
+
{
|
|
327
|
+
"enum": [
|
|
328
|
+
"high",
|
|
329
|
+
"low",
|
|
330
|
+
"max",
|
|
331
|
+
"medium",
|
|
332
|
+
"xhigh"
|
|
333
|
+
],
|
|
334
|
+
"type": "string"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"type": "null"
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
"blockParentFast": {
|
|
342
|
+
"type": "boolean"
|
|
343
|
+
},
|
|
344
|
+
"blockMode": {
|
|
345
|
+
"enum": [
|
|
346
|
+
"ask",
|
|
347
|
+
"deny"
|
|
348
|
+
],
|
|
349
|
+
"type": "string"
|
|
350
|
+
},
|
|
351
|
+
"readOnlyTypes": {
|
|
352
|
+
"type": "array",
|
|
353
|
+
"items": {
|
|
354
|
+
"type": "string"
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
"additionalProperties": false
|
|
359
|
+
},
|
|
360
|
+
"Partial<{command:string[]|null;severity:\"deny\"|\"warn\";}>": {
|
|
361
|
+
"type": "object",
|
|
362
|
+
"properties": {
|
|
363
|
+
"command": {
|
|
364
|
+
"description": "Null means the gate does not exist for this project.",
|
|
365
|
+
"anyOf": [
|
|
366
|
+
{
|
|
367
|
+
"type": "array",
|
|
368
|
+
"items": {
|
|
369
|
+
"type": "string"
|
|
370
|
+
}
|
|
371
|
+
},
|
|
372
|
+
{
|
|
373
|
+
"type": "null"
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
},
|
|
377
|
+
"severity": {
|
|
378
|
+
"enum": [
|
|
379
|
+
"deny",
|
|
380
|
+
"warn"
|
|
381
|
+
],
|
|
382
|
+
"type": "string"
|
|
383
|
+
}
|
|
384
|
+
},
|
|
385
|
+
"additionalProperties": false
|
|
386
|
+
},
|
|
387
|
+
"Partial<{enabled:boolean;onViolation:\"followup\"|\"off\";mode:import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).CommentMode;}>": {
|
|
388
|
+
"type": "object",
|
|
389
|
+
"properties": {
|
|
390
|
+
"enabled": {
|
|
391
|
+
"type": "boolean"
|
|
392
|
+
},
|
|
393
|
+
"onViolation": {
|
|
394
|
+
"enum": [
|
|
395
|
+
"followup",
|
|
396
|
+
"off"
|
|
397
|
+
],
|
|
398
|
+
"type": "string"
|
|
399
|
+
},
|
|
400
|
+
"mode": {
|
|
401
|
+
"description": "`declared` requires a reason marker, `strict` accepts no added comment at all, and `resolvable` is `declared`\nplus the question neither can ask: does the comment mean anything to a reader who was not in the session that\nwrote it ([/decisions/ad-070.md](/decisions/ad-070.md))?",
|
|
402
|
+
"enum": [
|
|
403
|
+
"declared",
|
|
404
|
+
"resolvable",
|
|
405
|
+
"strict"
|
|
406
|
+
],
|
|
407
|
+
"type": "string"
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
"additionalProperties": false
|
|
411
|
+
},
|
|
412
|
+
"Partial<{globalSpool:boolean;includePayloads:boolean;maxAttrChars:number;sessionCostAlertUsd:number|null;retentionDays:number;}>": {
|
|
413
|
+
"type": "object",
|
|
414
|
+
"properties": {
|
|
415
|
+
"globalSpool": {
|
|
416
|
+
"type": "boolean"
|
|
417
|
+
},
|
|
418
|
+
"includePayloads": {
|
|
419
|
+
"type": "boolean"
|
|
420
|
+
},
|
|
421
|
+
"maxAttrChars": {
|
|
422
|
+
"type": "number"
|
|
423
|
+
},
|
|
424
|
+
"sessionCostAlertUsd": {
|
|
425
|
+
"type": [
|
|
426
|
+
"null",
|
|
427
|
+
"number"
|
|
428
|
+
]
|
|
429
|
+
},
|
|
430
|
+
"retentionDays": {
|
|
431
|
+
"type": "number"
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
"additionalProperties": false
|
|
435
|
+
},
|
|
436
|
+
"Partial<{enabled:boolean;mode:import(\"./src/core/untrusted/untrusted.types\",{with:{\"resolution-mode\":\"import\"}}).UntrustedMode;extraTools:string[];extraCommandPatterns:string[];}>": {
|
|
437
|
+
"type": "object",
|
|
438
|
+
"properties": {
|
|
439
|
+
"enabled": {
|
|
440
|
+
"type": "boolean"
|
|
441
|
+
},
|
|
442
|
+
"mode": {
|
|
443
|
+
"description": "`frame` states once per turn that outside content is data. `enforce` adds the question framing cannot ask —\ndid this command come from that content ([/decisions/ad-077.md](/decisions/ad-077.md))?",
|
|
444
|
+
"enum": [
|
|
445
|
+
"enforce",
|
|
446
|
+
"frame"
|
|
447
|
+
],
|
|
448
|
+
"type": "string"
|
|
449
|
+
},
|
|
450
|
+
"extraTools": {
|
|
451
|
+
"type": "array",
|
|
452
|
+
"items": {
|
|
453
|
+
"type": "string"
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
"extraCommandPatterns": {
|
|
457
|
+
"type": "array",
|
|
458
|
+
"items": {
|
|
459
|
+
"type": "string"
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
"additionalProperties": false
|
|
464
|
+
},
|
|
465
|
+
"Partial<{enabled:boolean;windowMinutes:number;}>": {
|
|
466
|
+
"type": "object",
|
|
467
|
+
"properties": {
|
|
468
|
+
"enabled": {
|
|
469
|
+
"type": "boolean"
|
|
470
|
+
},
|
|
471
|
+
"windowMinutes": {
|
|
472
|
+
"type": "number"
|
|
473
|
+
}
|
|
474
|
+
},
|
|
475
|
+
"additionalProperties": false
|
|
476
|
+
},
|
|
477
|
+
"Partial<{enabled:boolean;}>": {
|
|
478
|
+
"type": "object",
|
|
479
|
+
"properties": {
|
|
480
|
+
"enabled": {
|
|
481
|
+
"type": "boolean"
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
"additionalProperties": false
|
|
485
|
+
},
|
|
486
|
+
"Partial<{catastrophicAsk:boolean;stallDetection:boolean;stallRepeatThreshold:number;}>": {
|
|
487
|
+
"type": "object",
|
|
488
|
+
"properties": {
|
|
489
|
+
"catastrophicAsk": {
|
|
490
|
+
"type": "boolean"
|
|
491
|
+
},
|
|
492
|
+
"stallDetection": {
|
|
493
|
+
"type": "boolean"
|
|
494
|
+
},
|
|
495
|
+
"stallRepeatThreshold": {
|
|
496
|
+
"type": "number"
|
|
497
|
+
}
|
|
498
|
+
},
|
|
499
|
+
"additionalProperties": false
|
|
500
|
+
},
|
|
501
|
+
"Partial<import(\"./src/core/policy/policy.types\",{with:{\"resolution-mode\":\"import\"}}).LessonsPolicyConfig>": {
|
|
502
|
+
"type": "object",
|
|
503
|
+
"properties": {
|
|
504
|
+
"enabled": {
|
|
505
|
+
"type": "boolean"
|
|
506
|
+
},
|
|
507
|
+
"maxInjectSession": {
|
|
508
|
+
"type": "number"
|
|
509
|
+
},
|
|
510
|
+
"maxInjectRetry": {
|
|
511
|
+
"type": "number"
|
|
512
|
+
},
|
|
513
|
+
"maxCharsSession": {
|
|
514
|
+
"type": "number"
|
|
515
|
+
},
|
|
516
|
+
"maxCharsRetry": {
|
|
517
|
+
"type": "number"
|
|
518
|
+
},
|
|
519
|
+
"promoteHitCount": {
|
|
520
|
+
"type": "number"
|
|
521
|
+
},
|
|
522
|
+
"decayLambda": {
|
|
523
|
+
"type": "number"
|
|
524
|
+
},
|
|
525
|
+
"projectBoost": {
|
|
526
|
+
"type": "number"
|
|
527
|
+
},
|
|
528
|
+
"syncRulesFile": {
|
|
529
|
+
"description": "How the durable provider view is decided. `auto` asks the host: a provider that does not deliver context from\nits session-start hook has no other route, so the view is written there and not where the hook works\n([/decisions/ad-050.md](/decisions/ad-050.md)).",
|
|
530
|
+
"enum": [
|
|
531
|
+
"always",
|
|
532
|
+
"auto",
|
|
533
|
+
"never"
|
|
534
|
+
],
|
|
535
|
+
"type": "string"
|
|
536
|
+
},
|
|
537
|
+
"gardenOnSessionEnd": {
|
|
538
|
+
"type": "boolean"
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
"additionalProperties": false
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
"$schema": "http://json-schema.org/draft-07/schema#"
|
|
545
|
+
}
|
|
@@ -27,8 +27,34 @@ export function loadCatalog(home = runtimeHome()): CapabilityCatalog | null {
|
|
|
27
27
|
return raw;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export type ConfigReadStatus =
|
|
31
|
+
| { status: "absent" }
|
|
32
|
+
| { status: "parsed"; value: Record<string, unknown> }
|
|
33
|
+
| { status: "malformed"; error: string };
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* why: `readJson` already reads this file but collapses "absent" and "malformed" to the same `null`
|
|
37
|
+
* — the exact silent-corruption gap `doctor` needs to close, so this is a second, tagged reader.
|
|
38
|
+
*/
|
|
39
|
+
export function readProjectPolicyStatus(projectDir: string): ConfigReadStatus {
|
|
40
|
+
const path = projectConfigPath(projectDir);
|
|
41
|
+
if (!existsSync(path)) {
|
|
42
|
+
return { status: "absent" };
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
|
|
46
|
+
// why: a JSON Schema meta-key, not a Policy field — see the identical strip in policy.loader.ts,
|
|
47
|
+
// this is the second, independent read of the same file.
|
|
48
|
+
delete parsed.$schema;
|
|
49
|
+
return { status: "parsed", value: parsed };
|
|
50
|
+
} catch (error) {
|
|
51
|
+
return { status: "malformed", error: error instanceof Error ? error.message : String(error) };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
30
55
|
export function readProjectPolicyRaw(projectDir: string): Record<string, unknown> | null {
|
|
31
|
-
|
|
56
|
+
const result = readProjectPolicyStatus(projectDir);
|
|
57
|
+
return result.status === "parsed" ? result.value : null;
|
|
32
58
|
}
|
|
33
59
|
|
|
34
60
|
function runtimeSeenPath(projectDir: string): string {
|
|
@@ -61,6 +61,15 @@ export function isCommentLine(text: string, file = ""): boolean {
|
|
|
61
61
|
return matchesSyntax(text, syntax) && !TOOL_DIRECTIVE.test(text);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/**
|
|
65
|
+
* invariant: no `codePaths` param. Taking one needs `policy.loader.ts` → `policy.defaults.ts` →
|
|
66
|
+
* `duplication.service.ts`, which imports this module back for `matchesSyntax` — an import cycle. The
|
|
67
|
+
* caller scopes by `codePaths` first; this filters what's left by syntax alone.
|
|
68
|
+
*/
|
|
69
|
+
export function filterCommentTargets(relativePaths: string[]): string[] {
|
|
70
|
+
return relativePaths.filter((path) => syntaxFor(path) !== null);
|
|
71
|
+
}
|
|
72
|
+
|
|
64
73
|
export function declaresReason(text: string): boolean {
|
|
65
74
|
return DECLARED_REASON.test(text);
|
|
66
75
|
}
|
|
@@ -155,14 +155,19 @@ export const COMMENT_SYNTAX: readonly CommentSyntaxEntry[] = [
|
|
|
155
155
|
extensions: [".sql"],
|
|
156
156
|
line: ["--"],
|
|
157
157
|
block: [["/*", "*/"]],
|
|
158
|
-
middle: ["*"]
|
|
158
|
+
// hazard: `middle: ["*"]` matched any line starting with a bare `*`, comment or not. In C-family languages
|
|
159
|
+
// that is safe — a line rarely opens with `*` outside a block continuation — but `SELECT *` does exactly
|
|
160
|
+
// that, so a multi-line query flagged its own projection as an undeclared comment.
|
|
161
|
+
middle: [],
|
|
159
162
|
},
|
|
160
163
|
{
|
|
161
164
|
id: "css",
|
|
162
165
|
extensions: [".css", ".scss", ".sass", ".less"],
|
|
163
166
|
line: ["//"],
|
|
164
167
|
block: [["/*", "*/"]],
|
|
165
|
-
|
|
168
|
+
// hazard: same defect as sql above — the universal selector `*` and a bare `*,` in a reset both start with
|
|
169
|
+
// `*`, so `middle: ["*"]` flagged ordinary CSS as an undeclared comment.
|
|
170
|
+
middle: [],
|
|
166
171
|
},
|
|
167
172
|
{
|
|
168
173
|
id: "yaml",
|
|
@@ -49,10 +49,19 @@ export function syntaxFor(file: string): CommentSyntax | null {
|
|
|
49
49
|
return lookupSyntax(file);
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// why: an image, a font, a lockfile or a test snapshot was never a candidate for a catalog entry — it carries
|
|
53
|
+
// no comment syntax to add. Reporting it as a coverage gap turns the signal an operator can act on into noise
|
|
54
|
+
// they learn to ignore.
|
|
55
|
+
const NOT_A_LANGUAGE =
|
|
56
|
+
/\.(png|jpe?g|gif|ico|bmp|webp|svg|woff2?|ttf|eot|otf|pdf|zip|gz|tgz|tar|lockb|snap)$/i;
|
|
57
|
+
|
|
52
58
|
/** why: the operator needs the coverage gap named. A language nobody listed is a rail that silently passes. */
|
|
53
59
|
export function unknownExtensions(files: readonly string[]): string[] {
|
|
54
60
|
const unknown = new Set<string>();
|
|
55
61
|
for (const file of files) {
|
|
62
|
+
if (NOT_A_LANGUAGE.test(file)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
56
65
|
if (lookupSyntax(file) === null) {
|
|
57
66
|
const name = file.toLowerCase().replace(/\\/g, "/").split("/").pop() ?? file;
|
|
58
67
|
const dot = name.lastIndexOf(".");
|
package/src/core/core.facade.ts
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
loadCatalog,
|
|
18
18
|
readProjectPolicyRaw,
|
|
19
|
+
readProjectPolicyStatus,
|
|
19
20
|
readRuntimeSeen,
|
|
20
21
|
writeRuntimeSeen,
|
|
21
22
|
} from "./capability/capability.store.ts";
|
|
@@ -23,6 +24,7 @@ import { ENABLE_HINT } from "./capability/capability.types.ts";
|
|
|
23
24
|
import {
|
|
24
25
|
commentViolationMessage,
|
|
25
26
|
declaresReason,
|
|
27
|
+
filterCommentTargets,
|
|
26
28
|
findAddedComments,
|
|
27
29
|
isCommentLine,
|
|
28
30
|
scanAddedComments,
|
|
@@ -132,7 +134,7 @@ import {
|
|
|
132
134
|
import { operatorBootstrapLines } from "./policy/policy.operator.ts";
|
|
133
135
|
import { isOperatorMode, OPERATOR_MODES } from "./policy/policy.posture.ts";
|
|
134
136
|
import { activeRails } from "./policy/policy.rails.ts";
|
|
135
|
-
import { pruneShadowed, shadowedKeys } from "./policy/policy.shadow.ts";
|
|
137
|
+
import { pruneShadowed, shadowedKeys, typeMismatches, unknownKeys } from "./policy/policy.shadow.ts";
|
|
136
138
|
import { forProvider } from "./policy/policy.types.ts";
|
|
137
139
|
import { checkCollision, heartbeat, register, release, sweepStale } from "./presence/presence.service.ts";
|
|
138
140
|
import { freshness, freshnessMessage, mayReplace, shouldRefetch } from "./pricing/pricing.freshness.ts";
|
|
@@ -263,6 +265,7 @@ export const coreFacade = {
|
|
|
263
265
|
ENABLE_HINT,
|
|
264
266
|
loadCatalog,
|
|
265
267
|
readProjectPolicyRaw,
|
|
268
|
+
readProjectPolicyStatus,
|
|
266
269
|
readRuntimeSeen,
|
|
267
270
|
writeRuntimeSeen,
|
|
268
271
|
isAvailableNotEnabled,
|
|
@@ -415,6 +418,8 @@ export const coreFacade = {
|
|
|
415
418
|
resolvedWithoutProjectTier,
|
|
416
419
|
shadowedKeys,
|
|
417
420
|
pruneShadowed,
|
|
421
|
+
unknownKeys,
|
|
422
|
+
typeMismatches,
|
|
418
423
|
resolveProjectPosture,
|
|
419
424
|
resolveProjectSyncMode,
|
|
420
425
|
OPERATOR_MODES,
|
|
@@ -448,6 +453,7 @@ export const coreFacade = {
|
|
|
448
453
|
isCommentLine,
|
|
449
454
|
declaresReason,
|
|
450
455
|
commentViolationMessage,
|
|
456
|
+
filterCommentTargets,
|
|
451
457
|
unknownExtensions,
|
|
452
458
|
KNOWN_EXTENSION_COUNT,
|
|
453
459
|
},
|