@windyroad/itil 0.35.1-preview.347 → 0.35.2-preview.349

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.
@@ -1,8 +1,10 @@
1
1
  {
2
2
  "description": "ITIL-aligned IT service management for Claude Code",
3
- "hooks": {
4
- "itil-assistant-output-gate": {
5
- "maturity": {
3
+ "maturity": {
4
+ "band": "Experimental",
5
+ "schema_version": "2.0",
6
+ "hooks": {
7
+ "itil-assistant-output-gate": {
6
8
  "band": "Experimental",
7
9
  "computed_at": "2026-05-18T01:29:14Z",
8
10
  "evidence": {
@@ -11,11 +13,9 @@
11
13
  "days_shipped": 32,
12
14
  "invocations_30d": null
13
15
  },
14
- "schema_version": "1.0"
15
- }
16
- },
17
- "itil-assistant-output-review": {
18
- "maturity": {
16
+ "schema_version": "2.0"
17
+ },
18
+ "itil-assistant-output-review": {
19
19
  "band": "Experimental",
20
20
  "computed_at": "2026-05-18T01:29:14Z",
21
21
  "evidence": {
@@ -24,11 +24,9 @@
24
24
  "days_shipped": 32,
25
25
  "invocations_30d": null
26
26
  },
27
- "schema_version": "1.0"
28
- }
29
- },
30
- "itil-bash-polling-antipattern-detect": {
31
- "maturity": {
27
+ "schema_version": "2.0"
28
+ },
29
+ "itil-bash-polling-antipattern-detect": {
32
30
  "band": "Experimental",
33
31
  "computed_at": "2026-05-18T01:29:14Z",
34
32
  "evidence": {
@@ -37,11 +35,9 @@
37
35
  "days_shipped": 32,
38
36
  "invocations_30d": null
39
37
  },
40
- "schema_version": "1.0"
41
- }
42
- },
43
- "itil-changeset-discipline": {
44
- "maturity": {
38
+ "schema_version": "2.0"
39
+ },
40
+ "itil-changeset-discipline": {
45
41
  "band": "Experimental",
46
42
  "computed_at": "2026-05-18T01:29:14Z",
47
43
  "evidence": {
@@ -50,11 +46,9 @@
50
46
  "days_shipped": 32,
51
47
  "invocations_30d": null
52
48
  },
53
- "schema_version": "1.0"
54
- }
55
- },
56
- "itil-claude-space-protection": {
57
- "maturity": {
49
+ "schema_version": "2.0"
50
+ },
51
+ "itil-claude-space-protection": {
58
52
  "band": "Experimental",
59
53
  "computed_at": "2026-05-18T01:29:14Z",
60
54
  "evidence": {
@@ -63,11 +57,9 @@
63
57
  "days_shipped": 32,
64
58
  "invocations_30d": null
65
59
  },
66
- "schema_version": "1.0"
67
- }
68
- },
69
- "itil-correction-detect": {
70
- "maturity": {
60
+ "schema_version": "2.0"
61
+ },
62
+ "itil-correction-detect": {
71
63
  "band": "Experimental",
72
64
  "computed_at": "2026-05-18T01:29:14Z",
73
65
  "evidence": {
@@ -76,11 +68,9 @@
76
68
  "days_shipped": 32,
77
69
  "invocations_30d": null
78
70
  },
79
- "schema_version": "1.0"
80
- }
81
- },
82
- "itil-fictional-defer-detect": {
83
- "maturity": {
71
+ "schema_version": "2.0"
72
+ },
73
+ "itil-fictional-defer-detect": {
84
74
  "band": "Experimental",
85
75
  "computed_at": "2026-05-18T01:29:14Z",
86
76
  "evidence": {
@@ -89,11 +79,9 @@
89
79
  "days_shipped": 32,
90
80
  "invocations_30d": null
91
81
  },
92
- "schema_version": "1.0"
93
- }
94
- },
95
- "itil-mid-loop-ask-detect": {
96
- "maturity": {
82
+ "schema_version": "2.0"
83
+ },
84
+ "itil-mid-loop-ask-detect": {
97
85
  "band": "Experimental",
98
86
  "computed_at": "2026-05-18T01:29:14Z",
99
87
  "evidence": {
@@ -102,11 +90,9 @@
102
90
  "days_shipped": 32,
103
91
  "invocations_30d": null
104
92
  },
105
- "schema_version": "1.0"
106
- }
107
- },
108
- "itil-pending-questions-surface": {
109
- "maturity": {
93
+ "schema_version": "2.0"
94
+ },
95
+ "itil-pending-questions-surface": {
110
96
  "band": "Experimental",
111
97
  "computed_at": "2026-05-18T01:29:14Z",
112
98
  "evidence": {
@@ -115,11 +101,9 @@
115
101
  "days_shipped": 32,
116
102
  "invocations_30d": null
117
103
  },
118
- "schema_version": "1.0"
119
- }
120
- },
121
- "itil-readme-refresh-discipline": {
122
- "maturity": {
104
+ "schema_version": "2.0"
105
+ },
106
+ "itil-readme-refresh-discipline": {
123
107
  "band": "Experimental",
124
108
  "computed_at": "2026-05-18T01:29:14Z",
125
109
  "evidence": {
@@ -128,11 +112,9 @@
128
112
  "days_shipped": 32,
129
113
  "invocations_30d": null
130
114
  },
131
- "schema_version": "1.0"
132
- }
133
- },
134
- "itil-rfc-trailer-advisory": {
135
- "maturity": {
115
+ "schema_version": "2.0"
116
+ },
117
+ "itil-rfc-trailer-advisory": {
136
118
  "band": "Experimental",
137
119
  "computed_at": "2026-05-18T01:29:14Z",
138
120
  "evidence": {
@@ -141,11 +123,9 @@
141
123
  "days_shipped": 32,
142
124
  "invocations_30d": null
143
125
  },
144
- "schema_version": "1.0"
145
- }
146
- },
147
- "itil-runtime-sid-marker": {
148
- "maturity": {
126
+ "schema_version": "2.0"
127
+ },
128
+ "itil-runtime-sid-marker": {
149
129
  "band": "Experimental",
150
130
  "computed_at": "2026-05-18T01:29:14Z",
151
131
  "evidence": {
@@ -154,11 +134,9 @@
154
134
  "days_shipped": 32,
155
135
  "invocations_30d": null
156
136
  },
157
- "schema_version": "1.0"
158
- }
159
- },
160
- "manage-problem-enforce-create": {
161
- "maturity": {
137
+ "schema_version": "2.0"
138
+ },
139
+ "manage-problem-enforce-create": {
162
140
  "band": "Experimental",
163
141
  "computed_at": "2026-05-18T01:29:14Z",
164
142
  "evidence": {
@@ -167,11 +145,9 @@
167
145
  "days_shipped": 32,
168
146
  "invocations_30d": null
169
147
  },
170
- "schema_version": "1.0"
171
- }
172
- },
173
- "p057-staging-trap-detect": {
174
- "maturity": {
148
+ "schema_version": "2.0"
149
+ },
150
+ "p057-staging-trap-detect": {
175
151
  "band": "Experimental",
176
152
  "computed_at": "2026-05-18T01:29:14Z",
177
153
  "evidence": {
@@ -180,11 +156,9 @@
180
156
  "days_shipped": 32,
181
157
  "invocations_30d": null
182
158
  },
183
- "schema_version": "1.0"
184
- }
185
- },
186
- "pre-publish-intake-gate": {
187
- "maturity": {
159
+ "schema_version": "2.0"
160
+ },
161
+ "pre-publish-intake-gate": {
188
162
  "band": "Experimental",
189
163
  "computed_at": "2026-05-18T01:29:14Z",
190
164
  "evidence": {
@@ -193,18 +167,11 @@
193
167
  "days_shipped": 32,
194
168
  "invocations_30d": null
195
169
  },
196
- "schema_version": "1.0"
170
+ "schema_version": "2.0"
197
171
  }
198
- }
199
- },
200
- "maturity": {
201
- "band": "Experimental",
202
- "schema_version": "1.0"
203
- },
204
- "name": "wr-itil",
205
- "skills": {
206
- "capture-problem": {
207
- "maturity": {
172
+ },
173
+ "skills": {
174
+ "capture-problem": {
208
175
  "band": "Experimental",
209
176
  "computed_at": "2026-05-18T01:29:14Z",
210
177
  "evidence": {
@@ -213,11 +180,9 @@
213
180
  "days_shipped": 32,
214
181
  "invocations_30d": 57
215
182
  },
216
- "schema_version": "1.0"
217
- }
218
- },
219
- "capture-rfc": {
220
- "maturity": {
183
+ "schema_version": "2.0"
184
+ },
185
+ "capture-rfc": {
221
186
  "band": "Experimental",
222
187
  "computed_at": "2026-05-18T01:29:14Z",
223
188
  "evidence": {
@@ -226,11 +191,9 @@
226
191
  "days_shipped": 32,
227
192
  "invocations_30d": 2
228
193
  },
229
- "schema_version": "1.0"
230
- }
231
- },
232
- "capture-story": {
233
- "maturity": {
194
+ "schema_version": "2.0"
195
+ },
196
+ "capture-story": {
234
197
  "band": "Experimental",
235
198
  "computed_at": "2026-05-18T01:29:14Z",
236
199
  "evidence": {
@@ -239,11 +202,9 @@
239
202
  "days_shipped": 32,
240
203
  "invocations_30d": 0
241
204
  },
242
- "schema_version": "1.0"
243
- }
244
- },
245
- "capture-story-map": {
246
- "maturity": {
205
+ "schema_version": "2.0"
206
+ },
207
+ "capture-story-map": {
247
208
  "band": "Experimental",
248
209
  "computed_at": "2026-05-18T01:29:14Z",
249
210
  "evidence": {
@@ -252,11 +213,9 @@
252
213
  "days_shipped": 32,
253
214
  "invocations_30d": 0
254
215
  },
255
- "schema_version": "1.0"
256
- }
257
- },
258
- "check-upstream-responses": {
259
- "maturity": {
216
+ "schema_version": "2.0"
217
+ },
218
+ "check-upstream-responses": {
260
219
  "band": "Experimental",
261
220
  "computed_at": "2026-05-18T01:29:14Z",
262
221
  "evidence": {
@@ -265,11 +224,9 @@
265
224
  "days_shipped": 32,
266
225
  "invocations_30d": 0
267
226
  },
268
- "schema_version": "1.0"
269
- }
270
- },
271
- "close-incident": {
272
- "maturity": {
227
+ "schema_version": "2.0"
228
+ },
229
+ "close-incident": {
273
230
  "band": "Experimental",
274
231
  "computed_at": "2026-05-18T01:29:14Z",
275
232
  "evidence": {
@@ -278,11 +235,9 @@
278
235
  "days_shipped": 32,
279
236
  "invocations_30d": 0
280
237
  },
281
- "schema_version": "1.0"
282
- }
283
- },
284
- "link-incident": {
285
- "maturity": {
238
+ "schema_version": "2.0"
239
+ },
240
+ "link-incident": {
286
241
  "band": "Experimental",
287
242
  "computed_at": "2026-05-18T01:29:14Z",
288
243
  "evidence": {
@@ -291,11 +246,9 @@
291
246
  "days_shipped": 32,
292
247
  "invocations_30d": 0
293
248
  },
294
- "schema_version": "1.0"
295
- }
296
- },
297
- "list-incidents": {
298
- "maturity": {
249
+ "schema_version": "2.0"
250
+ },
251
+ "list-incidents": {
299
252
  "band": "Experimental",
300
253
  "computed_at": "2026-05-18T01:29:14Z",
301
254
  "evidence": {
@@ -304,11 +257,9 @@
304
257
  "days_shipped": 32,
305
258
  "invocations_30d": 0
306
259
  },
307
- "schema_version": "1.0"
308
- }
309
- },
310
- "list-problems": {
311
- "maturity": {
260
+ "schema_version": "2.0"
261
+ },
262
+ "list-problems": {
312
263
  "band": "Experimental",
313
264
  "computed_at": "2026-05-18T01:29:14Z",
314
265
  "evidence": {
@@ -317,11 +268,9 @@
317
268
  "days_shipped": 32,
318
269
  "invocations_30d": 1
319
270
  },
320
- "schema_version": "1.0"
321
- }
322
- },
323
- "list-stories": {
324
- "maturity": {
271
+ "schema_version": "2.0"
272
+ },
273
+ "list-stories": {
325
274
  "band": "Experimental",
326
275
  "computed_at": "2026-05-18T01:29:14Z",
327
276
  "evidence": {
@@ -330,11 +279,9 @@
330
279
  "days_shipped": 32,
331
280
  "invocations_30d": 0
332
281
  },
333
- "schema_version": "1.0"
334
- }
335
- },
336
- "list-story-maps": {
337
- "maturity": {
282
+ "schema_version": "2.0"
283
+ },
284
+ "list-story-maps": {
338
285
  "band": "Experimental",
339
286
  "computed_at": "2026-05-18T01:29:14Z",
340
287
  "evidence": {
@@ -343,11 +290,9 @@
343
290
  "days_shipped": 32,
344
291
  "invocations_30d": 0
345
292
  },
346
- "schema_version": "1.0"
347
- }
348
- },
349
- "manage-incident": {
350
- "maturity": {
293
+ "schema_version": "2.0"
294
+ },
295
+ "manage-incident": {
351
296
  "band": "Experimental",
352
297
  "computed_at": "2026-05-18T01:29:14Z",
353
298
  "evidence": {
@@ -356,11 +301,9 @@
356
301
  "days_shipped": 32,
357
302
  "invocations_30d": 1
358
303
  },
359
- "schema_version": "1.0"
360
- }
361
- },
362
- "manage-problem": {
363
- "maturity": {
304
+ "schema_version": "2.0"
305
+ },
306
+ "manage-problem": {
364
307
  "band": "Alpha",
365
308
  "computed_at": "2026-05-18T01:29:14Z",
366
309
  "evidence": {
@@ -369,11 +312,9 @@
369
312
  "days_shipped": 32,
370
313
  "invocations_30d": 153
371
314
  },
372
- "schema_version": "1.0"
373
- }
374
- },
375
- "manage-rfc": {
376
- "maturity": {
315
+ "schema_version": "2.0"
316
+ },
317
+ "manage-rfc": {
377
318
  "band": "Experimental",
378
319
  "computed_at": "2026-05-18T01:29:14Z",
379
320
  "evidence": {
@@ -382,11 +323,9 @@
382
323
  "days_shipped": 32,
383
324
  "invocations_30d": 2
384
325
  },
385
- "schema_version": "1.0"
386
- }
387
- },
388
- "manage-story": {
389
- "maturity": {
326
+ "schema_version": "2.0"
327
+ },
328
+ "manage-story": {
390
329
  "band": "Experimental",
391
330
  "computed_at": "2026-05-18T01:29:14Z",
392
331
  "evidence": {
@@ -395,11 +334,9 @@
395
334
  "days_shipped": 32,
396
335
  "invocations_30d": 0
397
336
  },
398
- "schema_version": "1.0"
399
- }
400
- },
401
- "manage-story-map": {
402
- "maturity": {
337
+ "schema_version": "2.0"
338
+ },
339
+ "manage-story-map": {
403
340
  "band": "Experimental",
404
341
  "computed_at": "2026-05-18T01:29:14Z",
405
342
  "evidence": {
@@ -408,11 +345,9 @@
408
345
  "days_shipped": 32,
409
346
  "invocations_30d": 0
410
347
  },
411
- "schema_version": "1.0"
412
- }
413
- },
414
- "mitigate-incident": {
415
- "maturity": {
348
+ "schema_version": "2.0"
349
+ },
350
+ "mitigate-incident": {
416
351
  "band": "Experimental",
417
352
  "computed_at": "2026-05-18T01:29:14Z",
418
353
  "evidence": {
@@ -421,11 +356,9 @@
421
356
  "days_shipped": 32,
422
357
  "invocations_30d": 3
423
358
  },
424
- "schema_version": "1.0"
425
- }
426
- },
427
- "reconcile-readme": {
428
- "maturity": {
359
+ "schema_version": "2.0"
360
+ },
361
+ "reconcile-readme": {
429
362
  "band": "Experimental",
430
363
  "computed_at": "2026-05-18T01:29:14Z",
431
364
  "evidence": {
@@ -434,11 +367,9 @@
434
367
  "days_shipped": 32,
435
368
  "invocations_30d": 36
436
369
  },
437
- "schema_version": "1.0"
438
- }
439
- },
440
- "reconcile-stories": {
441
- "maturity": {
370
+ "schema_version": "2.0"
371
+ },
372
+ "reconcile-stories": {
442
373
  "band": "Experimental",
443
374
  "computed_at": "2026-05-18T01:29:14Z",
444
375
  "evidence": {
@@ -447,11 +378,9 @@
447
378
  "days_shipped": 32,
448
379
  "invocations_30d": 0
449
380
  },
450
- "schema_version": "1.0"
451
- }
452
- },
453
- "reconcile-story-maps": {
454
- "maturity": {
381
+ "schema_version": "2.0"
382
+ },
383
+ "reconcile-story-maps": {
455
384
  "band": "Experimental",
456
385
  "computed_at": "2026-05-18T01:29:14Z",
457
386
  "evidence": {
@@ -460,11 +389,9 @@
460
389
  "days_shipped": 32,
461
390
  "invocations_30d": 0
462
391
  },
463
- "schema_version": "1.0"
464
- }
465
- },
466
- "report-upstream": {
467
- "maturity": {
392
+ "schema_version": "2.0"
393
+ },
394
+ "report-upstream": {
468
395
  "band": "Experimental",
469
396
  "computed_at": "2026-05-18T01:29:14Z",
470
397
  "evidence": {
@@ -473,11 +400,9 @@
473
400
  "days_shipped": 32,
474
401
  "invocations_30d": 15
475
402
  },
476
- "schema_version": "1.0"
477
- }
478
- },
479
- "restore-incident": {
480
- "maturity": {
403
+ "schema_version": "2.0"
404
+ },
405
+ "restore-incident": {
481
406
  "band": "Experimental",
482
407
  "computed_at": "2026-05-18T01:29:14Z",
483
408
  "evidence": {
@@ -486,11 +411,9 @@
486
411
  "days_shipped": 32,
487
412
  "invocations_30d": 4
488
413
  },
489
- "schema_version": "1.0"
490
- }
491
- },
492
- "review-problems": {
493
- "maturity": {
414
+ "schema_version": "2.0"
415
+ },
416
+ "review-problems": {
494
417
  "band": "Experimental",
495
418
  "computed_at": "2026-05-18T01:29:14Z",
496
419
  "evidence": {
@@ -499,11 +422,9 @@
499
422
  "days_shipped": 32,
500
423
  "invocations_30d": 9
501
424
  },
502
- "schema_version": "1.0"
503
- }
504
- },
505
- "scaffold-intake": {
506
- "maturity": {
425
+ "schema_version": "2.0"
426
+ },
427
+ "scaffold-intake": {
507
428
  "band": "Experimental",
508
429
  "computed_at": "2026-05-18T01:29:14Z",
509
430
  "evidence": {
@@ -512,11 +433,9 @@
512
433
  "days_shipped": 32,
513
434
  "invocations_30d": 1
514
435
  },
515
- "schema_version": "1.0"
516
- }
517
- },
518
- "transition-problem": {
519
- "maturity": {
436
+ "schema_version": "2.0"
437
+ },
438
+ "transition-problem": {
520
439
  "band": "Experimental",
521
440
  "computed_at": "2026-05-18T01:29:14Z",
522
441
  "evidence": {
@@ -525,11 +444,9 @@
525
444
  "days_shipped": 32,
526
445
  "invocations_30d": 27
527
446
  },
528
- "schema_version": "1.0"
529
- }
530
- },
531
- "transition-problems": {
532
- "maturity": {
447
+ "schema_version": "2.0"
448
+ },
449
+ "transition-problems": {
533
450
  "band": "Experimental",
534
451
  "computed_at": "2026-05-18T01:29:14Z",
535
452
  "evidence": {
@@ -538,11 +455,9 @@
538
455
  "days_shipped": 32,
539
456
  "invocations_30d": 6
540
457
  },
541
- "schema_version": "1.0"
542
- }
543
- },
544
- "work-problem": {
545
- "maturity": {
458
+ "schema_version": "2.0"
459
+ },
460
+ "work-problem": {
546
461
  "band": "Experimental",
547
462
  "computed_at": "2026-05-18T01:29:14Z",
548
463
  "evidence": {
@@ -551,11 +466,9 @@
551
466
  "days_shipped": 32,
552
467
  "invocations_30d": 2
553
468
  },
554
- "schema_version": "1.0"
555
- }
556
- },
557
- "work-problems": {
558
- "maturity": {
469
+ "schema_version": "2.0"
470
+ },
471
+ "work-problems": {
559
472
  "band": "Experimental",
560
473
  "computed_at": "2026-05-18T01:29:14Z",
561
474
  "evidence": {
@@ -564,9 +477,10 @@
564
477
  "days_shipped": 32,
565
478
  "invocations_30d": 6
566
479
  },
567
- "schema_version": "1.0"
480
+ "schema_version": "2.0"
568
481
  }
569
482
  }
570
483
  },
571
- "version": "0.35.1"
484
+ "name": "wr-itil",
485
+ "version": "0.35.2"
572
486
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@windyroad/itil",
3
- "version": "0.35.1-preview.347",
3
+ "version": "0.35.2-preview.349",
4
4
  "description": "ITIL-aligned IT service management for Claude Code (problem, and future incident/change skills)",
5
5
  "bin": {
6
6
  "windyroad-itil": "./bin/install.mjs"
@@ -363,14 +363,17 @@ def build_surface_record(existing, kind, plugin_bare, name, evidence):
363
363
  if existing band is Deprecated, return existing record VERBATIM —
364
364
  do NOT recompute, do NOT update computed_at, do NOT overwrite
365
365
  supersededBy. All other records get a fresh recompute.
366
+
367
+ Under ADR-063 Amendment 2026-05-18 (P0 hotfix), `existing` IS the
368
+ maturity record directly (no inner `.maturity` envelope) when read
369
+ from the new nested location `plugin_doc["maturity"][<kind>][<name>]`.
366
370
  """
367
- existing_maturity = existing.get("maturity") if isinstance(existing, dict) else None
368
- if isinstance(existing_maturity, dict) and existing_maturity.get("band") == "Deprecated":
369
- return existing_maturity
371
+ if isinstance(existing, dict) and existing.get("band") == "Deprecated":
372
+ return existing
370
373
 
371
374
  band = compute_band(evidence)
372
375
  record = {
373
- "schema_version": "1.0",
376
+ "schema_version": "2.0",
374
377
  "band": band,
375
378
  "computed_at": now_canonical_iso,
376
379
  "evidence": {
@@ -406,15 +409,29 @@ for pkg_dir in plugin_dirs:
406
409
  surfaces = discover_surfaces(pkg_dir)
407
410
  total_plugins += 1
408
411
 
409
- # Per-kind surface maps.
412
+ # Per-kind surface maps. Records nest under `plugin_doc["maturity"][<key>]`
413
+ # (NOT top-level — Claude Code's plugin manifest validator rejects
414
+ # top-level `hooks:` / `skills:` / `agents:` / `commands:` keys with
415
+ # `Validation errors: hooks: Invalid input, skills: Invalid input` when
416
+ # they carry maturity-only records). Per ADR-063 Amendment 2026-05-18
417
+ # (P0 hotfix). Schema version bumps to "2.0" because the path move is
418
+ # NOT additive per ADR-058 §Confirmation #8.
410
419
  kind_to_key = {"skill": "skills", "agent": "agents", "hook": "hooks", "command": "commands"}
411
420
  surface_bands_for_rollup = []
412
421
 
422
+ # Pre-read existing nested maturity payload so re-runs preserve any
423
+ # forward-compat extras (architect issue 2: read-side line 417 fix).
424
+ existing_maturity = plugin_doc.get("maturity", {})
425
+ if not isinstance(existing_maturity, dict):
426
+ existing_maturity = {}
427
+
428
+ new_maturity_kinds = {}
429
+
413
430
  for kind, names in surfaces.items():
414
431
  if not names:
415
432
  continue
416
433
  key = kind_to_key[kind]
417
- existing_map = plugin_doc.get(key, {})
434
+ existing_map = existing_maturity.get(key, {})
418
435
  if not isinstance(existing_map, dict):
419
436
  existing_map = {}
420
437
  new_map = {}
@@ -426,24 +443,44 @@ for pkg_dir in plugin_dirs:
426
443
  "closed_tickets_window": exercise["closed_tickets_window"],
427
444
  "breaking_change_age_days": exercise["breaking_change_age_days"],
428
445
  }
446
+ # build_surface_record returns the maturity record directly (no
447
+ # outer `.maturity` envelope under the new nested shape).
429
448
  maturity_record = build_surface_record(existing_entry, kind, plugin_bare, name, evidence)
430
- # Preserve any extra keys on the existing entry (forward-compat).
431
- merged_entry = dict(existing_entry)
432
- merged_entry["maturity"] = maturity_record
433
- new_map[name] = merged_entry
449
+ # Schema_version stamped on each per-surface record.
450
+ maturity_record["schema_version"] = "2.0"
451
+ new_map[name] = maturity_record
434
452
  surface_bands_for_rollup.append(maturity_record.get("band"))
435
- plugin_doc[key] = new_map
453
+ new_maturity_kinds[key] = new_map
436
454
  wrote_records += len(new_map)
437
455
 
438
- # Plugin root rollup (ADR-063 §rollup schema: schema_version + band only).
456
+ # Plugin root rollup. ADR-063 Amendment 2026-05-18: per-kind surface
457
+ # maps nest UNDER `maturity:` alongside `schema_version` + `band`, not
458
+ # at top level. Write-ordering fix (architect issue 2 line 441): build
459
+ # the entire `maturity:` dict in a single assignment so the rollup
460
+ # write does not clobber the per-kind nested maps.
439
461
  rollup = rollup_band(surface_bands_for_rollup)
440
462
  if rollup is not None:
441
- plugin_doc["maturity"] = {"schema_version": "1.0", "band": rollup}
463
+ maturity_doc = {"schema_version": "2.0", "band": rollup}
464
+ maturity_doc.update(new_maturity_kinds)
465
+ plugin_doc["maturity"] = maturity_doc
442
466
  else:
443
467
  # Plugin with no shipped surfaces -> no plugin-level maturity field
444
468
  # (ADR-053 §granularity contract line 110).
445
469
  plugin_doc.pop("maturity", None)
446
470
 
471
+ # Defensive cleanup: strip any legacy top-level keys left over from the
472
+ # pre-Amendment 2026-05-18 broken shape so re-runs converge on the new
473
+ # layout. Only strip when the inner value is the maturity-only record
474
+ # map (pre-hotfix shape); preserve any other shape for safety.
475
+ for legacy_key in ("hooks", "skills", "agents", "commands"):
476
+ if legacy_key in plugin_doc:
477
+ inner = plugin_doc[legacy_key]
478
+ if isinstance(inner, dict) and all(
479
+ isinstance(v, dict) and set(v.keys()) <= {"maturity"}
480
+ for v in inner.values()
481
+ ):
482
+ del plugin_doc[legacy_key]
483
+
447
484
  # Serialise canonically: sorted keys + 2-space indent. Idempotency
448
485
  # depends on this stability (architect §H).
449
486
  new_text = json.dumps(plugin_doc, indent=2, sort_keys=True) + "\n"
@@ -344,16 +344,17 @@ def render_plugin(pkg_dir):
344
344
  readme_text = readme_path.read_text(encoding="utf-8")
345
345
  new_text = weave_rollup_into_lead_prose(readme_text, badge)
346
346
 
347
- # Build per-skill band map from the plugin.json `skills:` map.
347
+ # Build per-skill band map from the plugin.json `maturity.skills.<name>`
348
+ # nested location. Per ADR-063 Amendment 2026-05-18 (P0 hotfix), per-skill
349
+ # maturity records nest UNDER the top-level `maturity:` key as `band` /
350
+ # `schema_version` / `computed_at` / `evidence` (no outer `.maturity`
351
+ # envelope — the nested record IS the maturity record).
348
352
  skills_map = {}
349
- skills_section = plugin_doc.get("skills", {})
353
+ skills_section = maturity.get("skills", {}) if isinstance(maturity, dict) else {}
350
354
  if isinstance(skills_section, dict):
351
355
  for name, entry in skills_section.items():
352
- if not isinstance(entry, dict):
353
- continue
354
- entry_mat = entry.get("maturity")
355
- if isinstance(entry_mat, dict) and "band" in entry_mat:
356
- skills_map[name] = entry_mat["band"]
356
+ if isinstance(entry, dict) and "band" in entry:
357
+ skills_map[name] = entry["band"]
357
358
 
358
359
  new_text = populate_skills_column(new_text, skills_map)
359
360
 
@@ -200,7 +200,7 @@ print(obj if not isinstance(obj, (dict, list)) else json.dumps(obj))
200
200
 
201
201
  local pj="$PROJECT_ROOT/packages/alphap/.claude-plugin/plugin.json"
202
202
  local band
203
- band=$(get_json_field "$pj" "agents.agent.maturity.band")
203
+ band=$(get_json_field "$pj" "maturity.agents.agent.band")
204
204
  [ "$band" = "Alpha" ]
205
205
  }
206
206
 
@@ -220,7 +220,7 @@ print(obj if not isinstance(obj, (dict, list)) else json.dumps(obj))
220
220
 
221
221
  local pj="$PROJECT_ROOT/packages/expp/.claude-plugin/plugin.json"
222
222
  local band
223
- band=$(get_json_field "$pj" "skills.list-incidents.maturity.band")
223
+ band=$(get_json_field "$pj" "maturity.skills.list-incidents.band")
224
224
  [ "$band" = "Experimental" ]
225
225
  }
226
226
 
@@ -245,7 +245,7 @@ print(obj if not isinstance(obj, (dict, list)) else json.dumps(obj))
245
245
 
246
246
  local pj="$PROJECT_ROOT/packages/betap/.claude-plugin/plugin.json"
247
247
  local band
248
- band=$(get_json_field "$pj" "agents.agent.maturity.band")
248
+ band=$(get_json_field "$pj" "maturity.agents.agent.band")
249
249
  [ "$band" = "Beta" ]
250
250
  }
251
251
 
@@ -290,12 +290,12 @@ print(obj if not isinstance(obj, (dict, list)) else json.dumps(obj))
290
290
  [ "$status" -eq 0 ]
291
291
 
292
292
  local pj="$PROJECT_ROOT/packages/shapep/.claude-plugin/plugin.json"
293
- [ "$(get_json_field "$pj" "skills.manage-problem.maturity.schema_version")" = "1.0" ]
294
- [ "$(get_json_field "$pj" "skills.manage-problem.maturity.band")" != "MISSING" ]
295
- [ "$(get_json_field "$pj" "skills.manage-problem.maturity.computed_at")" != "MISSING" ]
296
- [ "$(get_json_field "$pj" "skills.manage-problem.maturity.evidence.invocations_30d")" = "100" ]
297
- [ "$(get_json_field "$pj" "skills.manage-problem.maturity.evidence.days_shipped")" = "25" ]
298
- [ "$(get_json_field "$pj" "skills.manage-problem.maturity.evidence.closed_tickets_window")" = "5" ]
293
+ [ "$(get_json_field "$pj" "maturity.skills.manage-problem.schema_version")" = "2.0" ]
294
+ [ "$(get_json_field "$pj" "maturity.skills.manage-problem.band")" != "MISSING" ]
295
+ [ "$(get_json_field "$pj" "maturity.skills.manage-problem.computed_at")" != "MISSING" ]
296
+ [ "$(get_json_field "$pj" "maturity.skills.manage-problem.evidence.invocations_30d")" = "100" ]
297
+ [ "$(get_json_field "$pj" "maturity.skills.manage-problem.evidence.days_shipped")" = "25" ]
298
+ [ "$(get_json_field "$pj" "maturity.skills.manage-problem.evidence.closed_tickets_window")" = "5" ]
299
299
  }
300
300
 
301
301
  @test "plugin-maturity-populate: plugin root rollup carries schema_version + band only, no evidence" {
@@ -315,7 +315,7 @@ print(obj if not isinstance(obj, (dict, list)) else json.dumps(obj))
315
315
  [ "$status" -eq 0 ]
316
316
 
317
317
  local pj="$PROJECT_ROOT/packages/rollupp/.claude-plugin/plugin.json"
318
- [ "$(get_json_field "$pj" "maturity.schema_version")" = "1.0" ]
318
+ [ "$(get_json_field "$pj" "maturity.schema_version")" = "2.0" ]
319
319
  [ "$(get_json_field "$pj" "maturity.band")" != "MISSING" ]
320
320
  [ "$(get_json_field "$pj" "maturity.evidence")" = "MISSING" ]
321
321
  }
@@ -370,7 +370,7 @@ print(obj if not isinstance(obj, (dict, list)) else json.dumps(obj))
370
370
  raw=$(python3 -c "
371
371
  import json
372
372
  obj = json.load(open('$pj'))
373
- print(json.dumps(obj['hooks']['itil-fictional-defer-detect']['maturity']['evidence']['invocations_30d']))
373
+ print(json.dumps(obj['maturity']['hooks']['itil-fictional-defer-detect']['evidence']['invocations_30d']))
374
374
  ")
375
375
  [ "$raw" = "null" ]
376
376
  }
@@ -387,19 +387,17 @@ print(json.dumps(obj['hooks']['itil-fictional-defer-detect']['maturity']['eviden
387
387
  python3 <<EOF
388
388
  import json
389
389
  obj = json.load(open("$pj"))
390
- obj.setdefault("skills", {})["oldskill"] = {
391
- "maturity": {
392
- "schema_version": "1.0",
393
- "band": "Deprecated",
394
- "computed_at": "2026-04-01T00:00:00Z",
395
- "supersededBy": "wr-depp:newskill",
396
- "evidence": {
397
- "invocations_30d": 50,
398
- "days_shipped": 100,
399
- "closed_tickets_window": 3,
400
- "breaking_change_age_days": None,
401
- },
402
- }
390
+ obj.setdefault("maturity", {}).setdefault("skills", {})["oldskill"] = {
391
+ "schema_version": "2.0",
392
+ "band": "Deprecated",
393
+ "computed_at": "2026-04-01T00:00:00Z",
394
+ "supersededBy": "wr-depp:newskill",
395
+ "evidence": {
396
+ "invocations_30d": 50,
397
+ "days_shipped": 100,
398
+ "closed_tickets_window": 3,
399
+ "breaking_change_age_days": None,
400
+ },
403
401
  }
404
402
  with open("$pj","w") as fh:
405
403
  json.dump(obj, fh, indent=2, sort_keys=True)
@@ -416,8 +414,8 @@ EOF
416
414
  --now=2026-05-17T12:00:00Z
417
415
  [ "$status" -eq 0 ]
418
416
 
419
- [ "$(get_json_field "$pj" "skills.oldskill.maturity.band")" = "Deprecated" ]
420
- [ "$(get_json_field "$pj" "skills.oldskill.maturity.supersededBy")" = "wr-depp:newskill" ]
417
+ [ "$(get_json_field "$pj" "maturity.skills.oldskill.band")" = "Deprecated" ]
418
+ [ "$(get_json_field "$pj" "maturity.skills.oldskill.supersededBy")" = "wr-depp:newskill" ]
421
419
  }
422
420
 
423
421
  # ── Bootstrapping clause sunset auto-derivation (architect adjustment D) ────
@@ -453,7 +451,7 @@ EOF
453
451
  # p1 aged 200d → Beta. p2 aged 25d → Beta-floor unmet (days <60), demotes
454
452
  # to Alpha steady-state OR Experimental depending on the days_shipped cell;
455
453
  # only assert here that bootstrapping is inactive (the p1 outcome).
456
- [ "$(get_json_field "$pj1" "agents.agent.maturity.band")" = "Beta" ]
454
+ [ "$(get_json_field "$pj1" "maturity.agents.agent.band")" = "Beta" ]
457
455
  }
458
456
 
459
457
  # ── ADR-013 Rule 6 fail-safe — missing NDJSON inputs ────────────────────────
@@ -156,11 +156,10 @@ make_plugin() {
156
156
  @test "per-skill column: adds Maturity column to existing Skills table" {
157
157
  make_plugin "stub" '{
158
158
  "name":"wr-stub","version":"0.1.0","description":"Stub",
159
- "maturity":{"schema_version":"1.0","band":"Alpha"},
160
- "skills":{
161
- "thing":{"maturity":{"schema_version":"1.0","band":"Alpha","computed_at":"2026-05-18T00:00:00Z","evidence":{"invocations_30d":50,"days_shipped":30,"closed_tickets_window":5,"breaking_change_age_days":null}}},
162
- "widget":{"maturity":{"schema_version":"1.0","band":"Experimental","computed_at":"2026-05-18T00:00:00Z","evidence":{"invocations_30d":2,"days_shipped":5,"closed_tickets_window":0,"breaking_change_age_days":null}}}
163
- }
159
+ "maturity":{"schema_version":"2.0","band":"Alpha","skills":{
160
+ "thing":{"schema_version":"2.0","band":"Alpha","computed_at":"2026-05-18T00:00:00Z","evidence":{"invocations_30d":50,"days_shipped":30,"closed_tickets_window":5,"breaking_change_age_days":null}},
161
+ "widget":{"schema_version":"2.0","band":"Experimental","computed_at":"2026-05-18T00:00:00Z","evidence":{"invocations_30d":2,"days_shipped":5,"closed_tickets_window":0,"breaking_change_age_days":null}}
162
+ }}
164
163
  }' \
165
164
  "# @windyroad/stub
166
165