backend-skeleton 1.4.0 → 1.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/README.md +113 -8
- package/bin/bskel.mjs +331 -53
- package/contracts/emit.mjs +22 -6
- package/contracts/openapi.mjs +125 -18
- package/handles/providers/java-spring/ast-bridge.mjs +85 -1
- package/handles/providers/java-spring/ast-helper/src/main/java/com/backendskeleton/asthelper/Main.java +407 -0
- package/handles/providers/java-spring/emit.mjs +126 -6
- package/handles/providers/java-spring/plan.mjs +244 -77
- package/handles/providers/java-spring/source-splice.mjs +477 -0
- package/handles/providers/java-spring/templates/AuthorizationPolicyStub.java.tmpl +30 -0
- package/handles/providers/java-spring/templates/HandleController.java.tmpl +21 -3
- package/handles/providers/java-spring/templates/ResourceResolver.java.tmpl +26 -0
- package/handles/providers/java-spring/templates/ResourceResolverPolicyStub.java.tmpl +9 -0
- package/handles/providers/java-spring/templates/ResourceResolverStub.java.tmpl +3 -3
- package/handles/providers/typescript-express/plan.mjs +15 -2
- package/lib/attest.mjs +59 -1
- package/lib/cli.mjs +79 -7
- package/lib/doctor.mjs +23 -0
- package/lib/exit-codes.mjs +17 -0
- package/lib/gate-definitions.mjs +65 -2
- package/lib/gate-export.mjs +199 -0
- package/lib/impact-export-graphify.mjs +145 -0
- package/lib/impact-graph.mjs +194 -0
- package/lib/impact-surface.mjs +158 -0
- package/lib/impact.mjs +286 -0
- package/lib/patch-kinds.mjs +24 -0
- package/lib/repo.mjs +46 -0
- package/lib/workflow.mjs +16 -0
- package/package.json +1 -1
- package/scanners/adapters/_java-spring-analyzer.mjs +55 -0
- package/scanners/adapters/java-spring.mjs +154 -3
- package/scanners/index.mjs +10 -0
- package/schemas/feature-contract.schema.json +12 -1
- package/schemas/gate-attestation.schema.json +6 -1
- package/schemas/gate-export.schema.json +530 -22
- package/schemas/handles-plan.schema.json +32 -0
- package/schemas/impact-baseline.schema.json +59 -0
- package/schemas/impact-graph.schema.json +53 -0
- package/schemas/impact-report.schema.json +86 -0
- package/schemas/impact-resolution.schema.json +33 -0
- package/schemas/java-source-splice.schema.json +84 -0
- package/schemas/patch-transaction.schema.json +87 -2
|
@@ -1,23 +1,271 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "urn:sbf:gate-export:
|
|
3
|
+
"$id": "urn:sbf:gate-export:2",
|
|
4
4
|
"title": "backend-skeleton gate export report (bskel gate export)",
|
|
5
|
-
"description": "D-gate-attestation-signing
|
|
5
|
+
"description": "D-attestation-payload-completeness: additive bump from sbf.gate-export/1 (D-gate-attestation-signing) -- every /1 field keeps its exact name, position, and meaning; new content (tool, artifacts, decisions, verdict, gates.*.live, and 5 new git.* fields) is added alongside. `current` mirrors schemas/state.schema.json's own per-gate record shape exactly (the raw, stored record getGate() returns -- status constrained to the 3 values ever WRITTEN to disk). `gates.*.live` is RECOMPUTED at export time (lib/verify.mjs's collectGateStatuses(), the same function `bskel verify` uses) so `current` (what was last written) and `live` (what is honestly true right now) can be compared side by side -- a gate whose stored record says pass but whose inputs have since changed shows current.status:\"pass\" and live.status:\"stale\" together. `additionalProperties` stays open on `gates` itself (keyed by gate name) so this schema never needs editing when a new gate is added -- GATE_NAMES in lib/gate-definitions.mjs is the one place that list lives.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
|
-
"required": [
|
|
8
|
+
"required": [
|
|
9
|
+
"schema",
|
|
10
|
+
"feature_id",
|
|
11
|
+
"generated_at",
|
|
12
|
+
"tool",
|
|
13
|
+
"git",
|
|
14
|
+
"artifacts",
|
|
15
|
+
"gates",
|
|
16
|
+
"decisions",
|
|
17
|
+
"verdict"
|
|
18
|
+
],
|
|
9
19
|
"properties": {
|
|
10
|
-
"schema": {
|
|
11
|
-
|
|
12
|
-
|
|
20
|
+
"schema": {
|
|
21
|
+
"const": "sbf.gate-export/3"
|
|
22
|
+
},
|
|
23
|
+
"feature_id": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"pattern": "^[0-9]{3}-[a-z0-9]+(-[a-z0-9]+)*$"
|
|
26
|
+
},
|
|
27
|
+
"generated_at": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"format": "date-time"
|
|
30
|
+
},
|
|
31
|
+
"tool": {
|
|
32
|
+
"type": "object",
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"required": [
|
|
35
|
+
"name",
|
|
36
|
+
"version",
|
|
37
|
+
"gate_names",
|
|
38
|
+
"canonicalization"
|
|
39
|
+
],
|
|
40
|
+
"properties": {
|
|
41
|
+
"name": {
|
|
42
|
+
"const": "bskel"
|
|
43
|
+
},
|
|
44
|
+
"version": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"description": "backend-skeleton's own package.json version -- lib/gate-export.mjs's toolVersion(), never the TARGET repo's version."
|
|
47
|
+
},
|
|
48
|
+
"gate_names": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
"description": "GATE_NAMES at export time -- lets a verifier on a different bskel build see that the signer's gate set differs from its own."
|
|
54
|
+
},
|
|
55
|
+
"canonicalization": {
|
|
56
|
+
"const": "sortkeysdeep-json",
|
|
57
|
+
"description": "lib/attest.mjs's CANONICALIZATION_ID -- the named algorithm this payload was canonicalized with before signing."
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
13
61
|
"git": {
|
|
14
62
|
"type": "object",
|
|
15
63
|
"additionalProperties": false,
|
|
16
|
-
"required": [
|
|
64
|
+
"required": [
|
|
65
|
+
"branch",
|
|
66
|
+
"head_sha",
|
|
67
|
+
"dirty",
|
|
68
|
+
"head_tree_sha",
|
|
69
|
+
"dirty_acknowledged",
|
|
70
|
+
"dirty_file_count",
|
|
71
|
+
"dirty_files_truncated",
|
|
72
|
+
"dirty_files"
|
|
73
|
+
],
|
|
17
74
|
"properties": {
|
|
18
|
-
"branch": {
|
|
19
|
-
|
|
20
|
-
|
|
75
|
+
"branch": {
|
|
76
|
+
"type": [
|
|
77
|
+
"string",
|
|
78
|
+
"null"
|
|
79
|
+
],
|
|
80
|
+
"description": "lib/repo.mjs's currentBranch() -- null on failure, or the literal string \"HEAD\" for a detached HEAD (not a real branch name)."
|
|
81
|
+
},
|
|
82
|
+
"head_sha": {
|
|
83
|
+
"type": "string"
|
|
84
|
+
},
|
|
85
|
+
"dirty": {
|
|
86
|
+
"type": [
|
|
87
|
+
"boolean",
|
|
88
|
+
"null"
|
|
89
|
+
],
|
|
90
|
+
"description": "lib/repo.mjs's isDirty()-equivalent (derived from worktreeStatus()) -- null on failure. true covers BOTH modified tracked files and untracked files (git status --porcelain, no -uno)."
|
|
91
|
+
},
|
|
92
|
+
"head_tree_sha": {
|
|
93
|
+
"type": [
|
|
94
|
+
"string",
|
|
95
|
+
"null"
|
|
96
|
+
],
|
|
97
|
+
"description": "git rev-parse HEAD^{tree} -- the CONTENT identity of HEAD, distinct from head_sha (its commit identity). null on git failure."
|
|
98
|
+
},
|
|
99
|
+
"dirty_acknowledged": {
|
|
100
|
+
"type": "boolean",
|
|
101
|
+
"description": "true iff `--allow-dirty` was passed to `gate export --sign` for THIS export -- see D-attestation-payload-completeness K4. Always false for an unsigned export."
|
|
102
|
+
},
|
|
103
|
+
"dirty_file_count": {
|
|
104
|
+
"type": "integer",
|
|
105
|
+
"minimum": 0
|
|
106
|
+
},
|
|
107
|
+
"dirty_files_truncated": {
|
|
108
|
+
"type": "boolean",
|
|
109
|
+
"description": "true iff dirty_file_count exceeds the cap and dirty_files below was truncated."
|
|
110
|
+
},
|
|
111
|
+
"dirty_files": {
|
|
112
|
+
"type": "array",
|
|
113
|
+
"description": "sorted by path, capped (see dirty_files_truncated). Deliberately NO file content hashes -- see D-attestation-payload-completeness's EXIT list.",
|
|
114
|
+
"items": {
|
|
115
|
+
"type": "object",
|
|
116
|
+
"additionalProperties": false,
|
|
117
|
+
"required": [
|
|
118
|
+
"status",
|
|
119
|
+
"path"
|
|
120
|
+
],
|
|
121
|
+
"properties": {
|
|
122
|
+
"status": {
|
|
123
|
+
"type": "string",
|
|
124
|
+
"description": "the raw 2-character `git status --porcelain` status code (e.g. \" M\", \"??\", \"R \")."
|
|
125
|
+
},
|
|
126
|
+
"path": {
|
|
127
|
+
"type": "string"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
"artifacts": {
|
|
135
|
+
"type": "object",
|
|
136
|
+
"additionalProperties": false,
|
|
137
|
+
"description": "D-attestation-payload-completeness (K2/K8): every key here comes from lib/gate-export.mjs's ARTIFACT_SOURCES table, unconditionally -- hashed FRESH at export time, independent of whether any gate ever passed. sha256 hex digest, or null when the file does not exist.",
|
|
138
|
+
"required": [
|
|
139
|
+
"scan_report_hash",
|
|
140
|
+
"spec_hash",
|
|
141
|
+
"contract_hash",
|
|
142
|
+
"contract_resolution_hash",
|
|
143
|
+
"openapi_snapshot_hash",
|
|
144
|
+
"cross_feature_report_hash",
|
|
145
|
+
"cross_feature_resolution_hash",
|
|
146
|
+
"dependencies_hash",
|
|
147
|
+
"rules_source_hash",
|
|
148
|
+
"rules_compiled_hash",
|
|
149
|
+
"conformance_report_hash",
|
|
150
|
+
"handles_manifest_hash",
|
|
151
|
+
"stack_record_hash",
|
|
152
|
+
"impact_baseline_hash",
|
|
153
|
+
"impact_report_hash",
|
|
154
|
+
"impact_resolution_hash"
|
|
155
|
+
],
|
|
156
|
+
"properties": {
|
|
157
|
+
"scan_report_hash": {
|
|
158
|
+
"type": [
|
|
159
|
+
"string",
|
|
160
|
+
"null"
|
|
161
|
+
],
|
|
162
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
163
|
+
},
|
|
164
|
+
"spec_hash": {
|
|
165
|
+
"type": [
|
|
166
|
+
"string",
|
|
167
|
+
"null"
|
|
168
|
+
],
|
|
169
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
170
|
+
},
|
|
171
|
+
"contract_hash": {
|
|
172
|
+
"type": [
|
|
173
|
+
"string",
|
|
174
|
+
"null"
|
|
175
|
+
],
|
|
176
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
177
|
+
},
|
|
178
|
+
"contract_resolution_hash": {
|
|
179
|
+
"type": [
|
|
180
|
+
"string",
|
|
181
|
+
"null"
|
|
182
|
+
],
|
|
183
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
184
|
+
},
|
|
185
|
+
"openapi_snapshot_hash": {
|
|
186
|
+
"type": [
|
|
187
|
+
"string",
|
|
188
|
+
"null"
|
|
189
|
+
],
|
|
190
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
191
|
+
},
|
|
192
|
+
"cross_feature_report_hash": {
|
|
193
|
+
"type": [
|
|
194
|
+
"string",
|
|
195
|
+
"null"
|
|
196
|
+
],
|
|
197
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
198
|
+
},
|
|
199
|
+
"cross_feature_resolution_hash": {
|
|
200
|
+
"type": [
|
|
201
|
+
"string",
|
|
202
|
+
"null"
|
|
203
|
+
],
|
|
204
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
205
|
+
},
|
|
206
|
+
"dependencies_hash": {
|
|
207
|
+
"type": [
|
|
208
|
+
"string",
|
|
209
|
+
"null"
|
|
210
|
+
],
|
|
211
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
212
|
+
},
|
|
213
|
+
"rules_source_hash": {
|
|
214
|
+
"type": [
|
|
215
|
+
"string",
|
|
216
|
+
"null"
|
|
217
|
+
],
|
|
218
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
219
|
+
},
|
|
220
|
+
"rules_compiled_hash": {
|
|
221
|
+
"type": [
|
|
222
|
+
"string",
|
|
223
|
+
"null"
|
|
224
|
+
],
|
|
225
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
226
|
+
},
|
|
227
|
+
"conformance_report_hash": {
|
|
228
|
+
"type": [
|
|
229
|
+
"string",
|
|
230
|
+
"null"
|
|
231
|
+
],
|
|
232
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
233
|
+
},
|
|
234
|
+
"handles_manifest_hash": {
|
|
235
|
+
"type": [
|
|
236
|
+
"string",
|
|
237
|
+
"null"
|
|
238
|
+
],
|
|
239
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
240
|
+
},
|
|
241
|
+
"stack_record_hash": {
|
|
242
|
+
"type": [
|
|
243
|
+
"string",
|
|
244
|
+
"null"
|
|
245
|
+
],
|
|
246
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
247
|
+
},
|
|
248
|
+
"impact_baseline_hash": {
|
|
249
|
+
"type": [
|
|
250
|
+
"string",
|
|
251
|
+
"null"
|
|
252
|
+
],
|
|
253
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
254
|
+
},
|
|
255
|
+
"impact_report_hash": {
|
|
256
|
+
"type": [
|
|
257
|
+
"string",
|
|
258
|
+
"null"
|
|
259
|
+
],
|
|
260
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
261
|
+
},
|
|
262
|
+
"impact_resolution_hash": {
|
|
263
|
+
"type": [
|
|
264
|
+
"string",
|
|
265
|
+
"null"
|
|
266
|
+
],
|
|
267
|
+
"pattern": "^[0-9a-f]{64}$"
|
|
268
|
+
}
|
|
21
269
|
}
|
|
22
270
|
},
|
|
23
271
|
"gates": {
|
|
@@ -25,32 +273,292 @@
|
|
|
25
273
|
"additionalProperties": {
|
|
26
274
|
"type": "object",
|
|
27
275
|
"additionalProperties": false,
|
|
28
|
-
"required": [
|
|
276
|
+
"required": [
|
|
277
|
+
"scope",
|
|
278
|
+
"current",
|
|
279
|
+
"live",
|
|
280
|
+
"history"
|
|
281
|
+
],
|
|
29
282
|
"properties": {
|
|
30
|
-
"scope": {
|
|
283
|
+
"scope": {
|
|
284
|
+
"type": "string",
|
|
285
|
+
"description": "the resolved scope id this gate was read from -- REPO_GATE_ID (\"_repo\") for a repo-scoped gate, or the feature_id itself."
|
|
286
|
+
},
|
|
31
287
|
"current": {
|
|
32
288
|
"anyOf": [
|
|
33
|
-
{
|
|
289
|
+
{
|
|
290
|
+
"type": "null"
|
|
291
|
+
},
|
|
34
292
|
{
|
|
35
293
|
"type": "object",
|
|
36
294
|
"additionalProperties": false,
|
|
37
|
-
"required": [
|
|
295
|
+
"required": [
|
|
296
|
+
"status",
|
|
297
|
+
"token",
|
|
298
|
+
"at"
|
|
299
|
+
],
|
|
38
300
|
"properties": {
|
|
39
|
-
"status": {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
301
|
+
"status": {
|
|
302
|
+
"enum": [
|
|
303
|
+
"pass",
|
|
304
|
+
"awaiting_disposition",
|
|
305
|
+
"revoked"
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
"token": {
|
|
309
|
+
"type": "string"
|
|
310
|
+
},
|
|
311
|
+
"at": {
|
|
312
|
+
"type": "string"
|
|
313
|
+
},
|
|
314
|
+
"forced": {
|
|
315
|
+
"type": "boolean"
|
|
316
|
+
},
|
|
317
|
+
"reason": {
|
|
318
|
+
"type": "string"
|
|
319
|
+
},
|
|
320
|
+
"evidence": {
|
|
321
|
+
"type": "object"
|
|
322
|
+
},
|
|
323
|
+
"inputs": {
|
|
324
|
+
"type": "object"
|
|
325
|
+
}
|
|
46
326
|
}
|
|
47
327
|
}
|
|
48
328
|
]
|
|
49
329
|
},
|
|
330
|
+
"live": {
|
|
331
|
+
"type": "object",
|
|
332
|
+
"additionalProperties": false,
|
|
333
|
+
"required": [
|
|
334
|
+
"policy",
|
|
335
|
+
"status",
|
|
336
|
+
"blocking",
|
|
337
|
+
"ran",
|
|
338
|
+
"current_token",
|
|
339
|
+
"stale_reason",
|
|
340
|
+
"changed_inputs"
|
|
341
|
+
],
|
|
342
|
+
"description": "RECOMPUTED at export time via the same lib/verify.mjs#collectGateStatuses() `bskel verify` uses -- the honest, read-time-derived verdict, as opposed to `current` (the raw stored record).",
|
|
343
|
+
"properties": {
|
|
344
|
+
"policy": {
|
|
345
|
+
"enum": [
|
|
346
|
+
"required",
|
|
347
|
+
"required-when-present"
|
|
348
|
+
]
|
|
349
|
+
},
|
|
350
|
+
"status": {
|
|
351
|
+
"enum": [
|
|
352
|
+
"pass",
|
|
353
|
+
"pass (forced)",
|
|
354
|
+
"stale",
|
|
355
|
+
"not_run",
|
|
356
|
+
"awaiting_disposition",
|
|
357
|
+
"revoked"
|
|
358
|
+
]
|
|
359
|
+
},
|
|
360
|
+
"blocking": {
|
|
361
|
+
"type": "boolean",
|
|
362
|
+
"description": "whether THIS gate's current live status blocks bskel verify's overall verdict (lib/verify.mjs#isBlockingGateResult)."
|
|
363
|
+
},
|
|
364
|
+
"ran": {
|
|
365
|
+
"type": "boolean"
|
|
366
|
+
},
|
|
367
|
+
"current_token": {
|
|
368
|
+
"type": [
|
|
369
|
+
"string",
|
|
370
|
+
"null"
|
|
371
|
+
]
|
|
372
|
+
},
|
|
373
|
+
"stale_reason": {
|
|
374
|
+
"type": [
|
|
375
|
+
"string",
|
|
376
|
+
"null"
|
|
377
|
+
],
|
|
378
|
+
"enum": [
|
|
379
|
+
"inputs_changed",
|
|
380
|
+
"no_recorded_inputs",
|
|
381
|
+
"recorded_inputs_mismatch",
|
|
382
|
+
"unknown",
|
|
383
|
+
"ttl_expired",
|
|
384
|
+
"invalid_timestamp",
|
|
385
|
+
null
|
|
386
|
+
]
|
|
387
|
+
},
|
|
388
|
+
"changed_inputs": {
|
|
389
|
+
"type": [
|
|
390
|
+
"array",
|
|
391
|
+
"null"
|
|
392
|
+
],
|
|
393
|
+
"items": {
|
|
394
|
+
"type": "string"
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
},
|
|
50
399
|
"history": {
|
|
51
400
|
"type": "array",
|
|
52
|
-
"items": {
|
|
401
|
+
"items": {
|
|
402
|
+
"type": "object"
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
},
|
|
408
|
+
"decisions": {
|
|
409
|
+
"type": "object",
|
|
410
|
+
"additionalProperties": false,
|
|
411
|
+
"required": [
|
|
412
|
+
"forced",
|
|
413
|
+
"revoked",
|
|
414
|
+
"waiver_files"
|
|
415
|
+
],
|
|
416
|
+
"description": "D-attestation-payload-completeness (K2): rolled up purely from the gate records already in `gates` above -- zero new file reads.",
|
|
417
|
+
"properties": {
|
|
418
|
+
"forced": {
|
|
419
|
+
"type": "array",
|
|
420
|
+
"items": {
|
|
421
|
+
"type": "object",
|
|
422
|
+
"additionalProperties": false,
|
|
423
|
+
"required": [
|
|
424
|
+
"gate",
|
|
425
|
+
"scope",
|
|
426
|
+
"reason",
|
|
427
|
+
"at"
|
|
428
|
+
],
|
|
429
|
+
"properties": {
|
|
430
|
+
"gate": {
|
|
431
|
+
"type": "string"
|
|
432
|
+
},
|
|
433
|
+
"scope": {
|
|
434
|
+
"type": "string"
|
|
435
|
+
},
|
|
436
|
+
"reason": {
|
|
437
|
+
"type": [
|
|
438
|
+
"string",
|
|
439
|
+
"null"
|
|
440
|
+
]
|
|
441
|
+
},
|
|
442
|
+
"at": {
|
|
443
|
+
"type": [
|
|
444
|
+
"string",
|
|
445
|
+
"null"
|
|
446
|
+
]
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
},
|
|
451
|
+
"revoked": {
|
|
452
|
+
"type": "array",
|
|
453
|
+
"items": {
|
|
454
|
+
"type": "object",
|
|
455
|
+
"additionalProperties": false,
|
|
456
|
+
"required": [
|
|
457
|
+
"gate",
|
|
458
|
+
"scope",
|
|
459
|
+
"reason",
|
|
460
|
+
"at"
|
|
461
|
+
],
|
|
462
|
+
"properties": {
|
|
463
|
+
"gate": {
|
|
464
|
+
"type": "string"
|
|
465
|
+
},
|
|
466
|
+
"scope": {
|
|
467
|
+
"type": "string"
|
|
468
|
+
},
|
|
469
|
+
"reason": {
|
|
470
|
+
"type": [
|
|
471
|
+
"string",
|
|
472
|
+
"null"
|
|
473
|
+
]
|
|
474
|
+
},
|
|
475
|
+
"at": {
|
|
476
|
+
"type": [
|
|
477
|
+
"string",
|
|
478
|
+
"null"
|
|
479
|
+
]
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
"waiver_files": {
|
|
485
|
+
"type": "object",
|
|
486
|
+
"additionalProperties": false,
|
|
487
|
+
"required": [
|
|
488
|
+
"contract_resolution",
|
|
489
|
+
"cross_feature_resolution"
|
|
490
|
+
],
|
|
491
|
+
"description": "presence + hash only -- the CONTENT is already bound via `artifacts` above (contract_resolution_hash / cross_feature_resolution_hash), never embedded here a second time.",
|
|
492
|
+
"properties": {
|
|
493
|
+
"contract_resolution": {
|
|
494
|
+
"type": "object",
|
|
495
|
+
"additionalProperties": false,
|
|
496
|
+
"required": [
|
|
497
|
+
"present",
|
|
498
|
+
"hash"
|
|
499
|
+
],
|
|
500
|
+
"properties": {
|
|
501
|
+
"present": {
|
|
502
|
+
"type": "boolean"
|
|
503
|
+
},
|
|
504
|
+
"hash": {
|
|
505
|
+
"type": [
|
|
506
|
+
"string",
|
|
507
|
+
"null"
|
|
508
|
+
]
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
},
|
|
512
|
+
"cross_feature_resolution": {
|
|
513
|
+
"type": "object",
|
|
514
|
+
"additionalProperties": false,
|
|
515
|
+
"required": [
|
|
516
|
+
"present",
|
|
517
|
+
"hash"
|
|
518
|
+
],
|
|
519
|
+
"properties": {
|
|
520
|
+
"present": {
|
|
521
|
+
"type": "boolean"
|
|
522
|
+
},
|
|
523
|
+
"hash": {
|
|
524
|
+
"type": [
|
|
525
|
+
"string",
|
|
526
|
+
"null"
|
|
527
|
+
]
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
},
|
|
535
|
+
"verdict": {
|
|
536
|
+
"type": "object",
|
|
537
|
+
"additionalProperties": false,
|
|
538
|
+
"required": [
|
|
539
|
+
"blocking_gates",
|
|
540
|
+
"passing",
|
|
541
|
+
"total",
|
|
542
|
+
"ok"
|
|
543
|
+
],
|
|
544
|
+
"description": "D-attestation-payload-completeness (K2/K3): pure derivation from gates[*].live -- what a human or CI actually greps for.",
|
|
545
|
+
"properties": {
|
|
546
|
+
"blocking_gates": {
|
|
547
|
+
"type": "array",
|
|
548
|
+
"items": {
|
|
549
|
+
"type": "string"
|
|
53
550
|
}
|
|
551
|
+
},
|
|
552
|
+
"passing": {
|
|
553
|
+
"type": "integer",
|
|
554
|
+
"minimum": 0
|
|
555
|
+
},
|
|
556
|
+
"total": {
|
|
557
|
+
"type": "integer",
|
|
558
|
+
"minimum": 0
|
|
559
|
+
},
|
|
560
|
+
"ok": {
|
|
561
|
+
"type": "boolean"
|
|
54
562
|
}
|
|
55
563
|
}
|
|
56
564
|
}
|
|
@@ -25,6 +25,38 @@
|
|
|
25
25
|
"readPath": { "type": ["string", "null"] },
|
|
26
26
|
"requiredAuthority": { "type": "string" },
|
|
27
27
|
"requiredAuthorityForPatch": { "type": "string", "description": "O5 (D-resolver-authorization-action-aware): java-spring only -- derived independently from the entity's UPDATE endpoint, not copied from requiredAuthority (which is fetch/recover's own value)." },
|
|
28
|
+
"policies": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"description": "D-resolver-policy-contract (PC2): java-spring only -- exactly two records, action 'fetch' then 'patch'. 'recover' has no record of its own: it is governed by the 'fetch' record (HandleController#recover uses requiredAuthority()), though the runtime authorize() call still receives the distinct action string \"recover\". requiredAuthority/requiredAuthorityForPatch are the legacy scalar PROJECTION of these records; policies is the source of truth.",
|
|
31
|
+
"minItems": 2,
|
|
32
|
+
"maxItems": 2,
|
|
33
|
+
"items": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"required": ["action", "mode", "status", "authority", "evidence", "reason"],
|
|
37
|
+
"properties": {
|
|
38
|
+
"action": { "enum": ["fetch", "patch"] },
|
|
39
|
+
"mode": { "enum": ["role", "delegated"] },
|
|
40
|
+
"status": { "enum": ["materialized", "unresolved"] },
|
|
41
|
+
"authority": { "type": ["string", "null"] },
|
|
42
|
+
"reason": { "type": ["string", "null"] },
|
|
43
|
+
"evidence": {
|
|
44
|
+
"type": "object",
|
|
45
|
+
"additionalProperties": false,
|
|
46
|
+
"required": ["kind", "scope", "file", "line", "symbol", "text"],
|
|
47
|
+
"properties": {
|
|
48
|
+
"kind": { "enum": ["endpoint-absent", "endpoint-method-absent", "companion-annotation-present", "pre-authorize-ambiguous", "pre-authorize-unrecognized", "authorization-annotation-absent", "pre-authorize-has-role", "pre-authorize-has-authority"] },
|
|
49
|
+
"scope": { "enum": ["method", "class", null] },
|
|
50
|
+
"file": { "type": ["string", "null"] },
|
|
51
|
+
"line": { "type": ["integer", "null"], "minimum": 1 },
|
|
52
|
+
"symbol": { "type": ["string", "null"] },
|
|
53
|
+
"text": { "type": ["string", "null"] }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
"authorizationMode": { "type": "string", "enum": ["role", "delegated"], "description": "D-resolver-policy-contract (PC2): java-spring only -- 'delegated' whenever either policies[] record is unresolved. Drives HandleController.java.tmpl's authorizeOrThrow(): 'role' keeps the historical requireAuthority() equality check, 'delegated' skips it and requires an injected AuthorizationPolicy bean instead." },
|
|
28
60
|
"willGenerateResolver": { "type": "boolean" }
|
|
29
61
|
}
|
|
30
62
|
}
|