@mc-and-his-agents/loom-installer 0.1.39 → 0.1.40
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/package.json +1 -1
- package/payload/manifest.json +62 -62
- package/payload/plugin/loom/skills/shared/scripts/fact_chain_support.py +34 -0
- package/payload/plugin/loom/skills/shared/scripts/loom_check.py +82 -0
- package/payload/plugin/loom/skills/shared/scripts/loom_flow.py +53 -15
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/fact_chain_support.py +34 -0
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py +82 -0
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_flow.py +53 -15
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/fact_chain_support.py +34 -0
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py +82 -0
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_flow.py +53 -15
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/fact_chain_support.py +34 -0
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_check.py +82 -0
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_flow.py +53 -15
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/fact_chain_support.py +34 -0
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py +82 -0
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_flow.py +53 -15
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/fact_chain_support.py +34 -0
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py +82 -0
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_flow.py +53 -15
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/fact_chain_support.py +34 -0
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py +82 -0
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_flow.py +53 -15
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/fact_chain_support.py +34 -0
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py +82 -0
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_flow.py +53 -15
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/fact_chain_support.py +34 -0
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_check.py +82 -0
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_flow.py +53 -15
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/fact_chain_support.py +34 -0
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py +82 -0
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_flow.py +53 -15
package/package.json
CHANGED
package/payload/manifest.json
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"schema_version": "loom-installer-payload/v1",
|
|
3
3
|
"loom_version": "0.4.0",
|
|
4
4
|
"source_repository": "https://github.com/MC-and-his-Agents/Loom",
|
|
5
|
-
"source_commit": "
|
|
5
|
+
"source_commit": "59a29b0ae0fb79827af5bb3de6def984b132c398",
|
|
6
6
|
"source_ref": "main",
|
|
7
|
-
"built_at": "2026-04-
|
|
7
|
+
"built_at": "2026-04-27T17:07:01+08:00",
|
|
8
8
|
"runtime": {
|
|
9
9
|
"python_minimum": "3.10",
|
|
10
10
|
"python_recommended": "3.11+"
|
|
@@ -623,8 +623,8 @@
|
|
|
623
623
|
},
|
|
624
624
|
{
|
|
625
625
|
"path": "plugin/loom/skills/shared/scripts/fact_chain_support.py",
|
|
626
|
-
"bytes":
|
|
627
|
-
"sha256": "
|
|
626
|
+
"bytes": 22966,
|
|
627
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
628
628
|
},
|
|
629
629
|
{
|
|
630
630
|
"path": "plugin/loom/skills/shared/scripts/governance_surface.py",
|
|
@@ -633,13 +633,13 @@
|
|
|
633
633
|
},
|
|
634
634
|
{
|
|
635
635
|
"path": "plugin/loom/skills/shared/scripts/loom_check.py",
|
|
636
|
-
"bytes":
|
|
637
|
-
"sha256": "
|
|
636
|
+
"bytes": 344185,
|
|
637
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
638
638
|
},
|
|
639
639
|
{
|
|
640
640
|
"path": "plugin/loom/skills/shared/scripts/loom_flow.py",
|
|
641
|
-
"bytes":
|
|
642
|
-
"sha256": "
|
|
641
|
+
"bytes": 335994,
|
|
642
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
643
643
|
},
|
|
644
644
|
{
|
|
645
645
|
"path": "plugin/loom/skills/shared/scripts/loom_init.py",
|
|
@@ -1213,8 +1213,8 @@
|
|
|
1213
1213
|
},
|
|
1214
1214
|
{
|
|
1215
1215
|
"path": "skills/loom-adopt/.loom-runtime/shared/scripts/fact_chain_support.py",
|
|
1216
|
-
"bytes":
|
|
1217
|
-
"sha256": "
|
|
1216
|
+
"bytes": 22966,
|
|
1217
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
1218
1218
|
},
|
|
1219
1219
|
{
|
|
1220
1220
|
"path": "skills/loom-adopt/.loom-runtime/shared/scripts/governance_surface.py",
|
|
@@ -1223,13 +1223,13 @@
|
|
|
1223
1223
|
},
|
|
1224
1224
|
{
|
|
1225
1225
|
"path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py",
|
|
1226
|
-
"bytes":
|
|
1227
|
-
"sha256": "
|
|
1226
|
+
"bytes": 344185,
|
|
1227
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
1228
1228
|
},
|
|
1229
1229
|
{
|
|
1230
1230
|
"path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_flow.py",
|
|
1231
|
-
"bytes":
|
|
1232
|
-
"sha256": "
|
|
1231
|
+
"bytes": 335994,
|
|
1232
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
1233
1233
|
},
|
|
1234
1234
|
{
|
|
1235
1235
|
"path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -1833,8 +1833,8 @@
|
|
|
1833
1833
|
},
|
|
1834
1834
|
{
|
|
1835
1835
|
"path": "skills/loom-handoff/.loom-runtime/shared/scripts/fact_chain_support.py",
|
|
1836
|
-
"bytes":
|
|
1837
|
-
"sha256": "
|
|
1836
|
+
"bytes": 22966,
|
|
1837
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
1838
1838
|
},
|
|
1839
1839
|
{
|
|
1840
1840
|
"path": "skills/loom-handoff/.loom-runtime/shared/scripts/governance_surface.py",
|
|
@@ -1843,13 +1843,13 @@
|
|
|
1843
1843
|
},
|
|
1844
1844
|
{
|
|
1845
1845
|
"path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py",
|
|
1846
|
-
"bytes":
|
|
1847
|
-
"sha256": "
|
|
1846
|
+
"bytes": 344185,
|
|
1847
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
1848
1848
|
},
|
|
1849
1849
|
{
|
|
1850
1850
|
"path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_flow.py",
|
|
1851
|
-
"bytes":
|
|
1852
|
-
"sha256": "
|
|
1851
|
+
"bytes": 335994,
|
|
1852
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
1853
1853
|
},
|
|
1854
1854
|
{
|
|
1855
1855
|
"path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -2453,8 +2453,8 @@
|
|
|
2453
2453
|
},
|
|
2454
2454
|
{
|
|
2455
2455
|
"path": "skills/loom-init/.loom-runtime/shared/scripts/fact_chain_support.py",
|
|
2456
|
-
"bytes":
|
|
2457
|
-
"sha256": "
|
|
2456
|
+
"bytes": 22966,
|
|
2457
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
2458
2458
|
},
|
|
2459
2459
|
{
|
|
2460
2460
|
"path": "skills/loom-init/.loom-runtime/shared/scripts/governance_surface.py",
|
|
@@ -2463,13 +2463,13 @@
|
|
|
2463
2463
|
},
|
|
2464
2464
|
{
|
|
2465
2465
|
"path": "skills/loom-init/.loom-runtime/shared/scripts/loom_check.py",
|
|
2466
|
-
"bytes":
|
|
2467
|
-
"sha256": "
|
|
2466
|
+
"bytes": 344185,
|
|
2467
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
2468
2468
|
},
|
|
2469
2469
|
{
|
|
2470
2470
|
"path": "skills/loom-init/.loom-runtime/shared/scripts/loom_flow.py",
|
|
2471
|
-
"bytes":
|
|
2472
|
-
"sha256": "
|
|
2471
|
+
"bytes": 335994,
|
|
2472
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
2473
2473
|
},
|
|
2474
2474
|
{
|
|
2475
2475
|
"path": "skills/loom-init/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -3078,8 +3078,8 @@
|
|
|
3078
3078
|
},
|
|
3079
3079
|
{
|
|
3080
3080
|
"path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/fact_chain_support.py",
|
|
3081
|
-
"bytes":
|
|
3082
|
-
"sha256": "
|
|
3081
|
+
"bytes": 22966,
|
|
3082
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
3083
3083
|
},
|
|
3084
3084
|
{
|
|
3085
3085
|
"path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/governance_surface.py",
|
|
@@ -3088,13 +3088,13 @@
|
|
|
3088
3088
|
},
|
|
3089
3089
|
{
|
|
3090
3090
|
"path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py",
|
|
3091
|
-
"bytes":
|
|
3092
|
-
"sha256": "
|
|
3091
|
+
"bytes": 344185,
|
|
3092
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
3093
3093
|
},
|
|
3094
3094
|
{
|
|
3095
3095
|
"path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_flow.py",
|
|
3096
|
-
"bytes":
|
|
3097
|
-
"sha256": "
|
|
3096
|
+
"bytes": 335994,
|
|
3097
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
3098
3098
|
},
|
|
3099
3099
|
{
|
|
3100
3100
|
"path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -3698,8 +3698,8 @@
|
|
|
3698
3698
|
},
|
|
3699
3699
|
{
|
|
3700
3700
|
"path": "skills/loom-pre-review/.loom-runtime/shared/scripts/fact_chain_support.py",
|
|
3701
|
-
"bytes":
|
|
3702
|
-
"sha256": "
|
|
3701
|
+
"bytes": 22966,
|
|
3702
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
3703
3703
|
},
|
|
3704
3704
|
{
|
|
3705
3705
|
"path": "skills/loom-pre-review/.loom-runtime/shared/scripts/governance_surface.py",
|
|
@@ -3708,13 +3708,13 @@
|
|
|
3708
3708
|
},
|
|
3709
3709
|
{
|
|
3710
3710
|
"path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py",
|
|
3711
|
-
"bytes":
|
|
3712
|
-
"sha256": "
|
|
3711
|
+
"bytes": 344185,
|
|
3712
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
3713
3713
|
},
|
|
3714
3714
|
{
|
|
3715
3715
|
"path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_flow.py",
|
|
3716
|
-
"bytes":
|
|
3717
|
-
"sha256": "
|
|
3716
|
+
"bytes": 335994,
|
|
3717
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
3718
3718
|
},
|
|
3719
3719
|
{
|
|
3720
3720
|
"path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -4318,8 +4318,8 @@
|
|
|
4318
4318
|
},
|
|
4319
4319
|
{
|
|
4320
4320
|
"path": "skills/loom-resume/.loom-runtime/shared/scripts/fact_chain_support.py",
|
|
4321
|
-
"bytes":
|
|
4322
|
-
"sha256": "
|
|
4321
|
+
"bytes": 22966,
|
|
4322
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
4323
4323
|
},
|
|
4324
4324
|
{
|
|
4325
4325
|
"path": "skills/loom-resume/.loom-runtime/shared/scripts/governance_surface.py",
|
|
@@ -4328,13 +4328,13 @@
|
|
|
4328
4328
|
},
|
|
4329
4329
|
{
|
|
4330
4330
|
"path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py",
|
|
4331
|
-
"bytes":
|
|
4332
|
-
"sha256": "
|
|
4331
|
+
"bytes": 344185,
|
|
4332
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
4333
4333
|
},
|
|
4334
4334
|
{
|
|
4335
4335
|
"path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_flow.py",
|
|
4336
|
-
"bytes":
|
|
4337
|
-
"sha256": "
|
|
4336
|
+
"bytes": 335994,
|
|
4337
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
4338
4338
|
},
|
|
4339
4339
|
{
|
|
4340
4340
|
"path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -4938,8 +4938,8 @@
|
|
|
4938
4938
|
},
|
|
4939
4939
|
{
|
|
4940
4940
|
"path": "skills/loom-retire/.loom-runtime/shared/scripts/fact_chain_support.py",
|
|
4941
|
-
"bytes":
|
|
4942
|
-
"sha256": "
|
|
4941
|
+
"bytes": 22966,
|
|
4942
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
4943
4943
|
},
|
|
4944
4944
|
{
|
|
4945
4945
|
"path": "skills/loom-retire/.loom-runtime/shared/scripts/governance_surface.py",
|
|
@@ -4948,13 +4948,13 @@
|
|
|
4948
4948
|
},
|
|
4949
4949
|
{
|
|
4950
4950
|
"path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py",
|
|
4951
|
-
"bytes":
|
|
4952
|
-
"sha256": "
|
|
4951
|
+
"bytes": 344185,
|
|
4952
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
4953
4953
|
},
|
|
4954
4954
|
{
|
|
4955
4955
|
"path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_flow.py",
|
|
4956
|
-
"bytes":
|
|
4957
|
-
"sha256": "
|
|
4956
|
+
"bytes": 335994,
|
|
4957
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
4958
4958
|
},
|
|
4959
4959
|
{
|
|
4960
4960
|
"path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -5558,8 +5558,8 @@
|
|
|
5558
5558
|
},
|
|
5559
5559
|
{
|
|
5560
5560
|
"path": "skills/loom-review/.loom-runtime/shared/scripts/fact_chain_support.py",
|
|
5561
|
-
"bytes":
|
|
5562
|
-
"sha256": "
|
|
5561
|
+
"bytes": 22966,
|
|
5562
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
5563
5563
|
},
|
|
5564
5564
|
{
|
|
5565
5565
|
"path": "skills/loom-review/.loom-runtime/shared/scripts/governance_surface.py",
|
|
@@ -5568,13 +5568,13 @@
|
|
|
5568
5568
|
},
|
|
5569
5569
|
{
|
|
5570
5570
|
"path": "skills/loom-review/.loom-runtime/shared/scripts/loom_check.py",
|
|
5571
|
-
"bytes":
|
|
5572
|
-
"sha256": "
|
|
5571
|
+
"bytes": 344185,
|
|
5572
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
5573
5573
|
},
|
|
5574
5574
|
{
|
|
5575
5575
|
"path": "skills/loom-review/.loom-runtime/shared/scripts/loom_flow.py",
|
|
5576
|
-
"bytes":
|
|
5577
|
-
"sha256": "
|
|
5576
|
+
"bytes": 335994,
|
|
5577
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
5578
5578
|
},
|
|
5579
5579
|
{
|
|
5580
5580
|
"path": "skills/loom-review/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -6178,8 +6178,8 @@
|
|
|
6178
6178
|
},
|
|
6179
6179
|
{
|
|
6180
6180
|
"path": "skills/loom-spec-review/.loom-runtime/shared/scripts/fact_chain_support.py",
|
|
6181
|
-
"bytes":
|
|
6182
|
-
"sha256": "
|
|
6181
|
+
"bytes": 22966,
|
|
6182
|
+
"sha256": "78cf0d0022a4c03933a5e46cba676e80cafa5d0802fb95191a761ec9f9417e65"
|
|
6183
6183
|
},
|
|
6184
6184
|
{
|
|
6185
6185
|
"path": "skills/loom-spec-review/.loom-runtime/shared/scripts/governance_surface.py",
|
|
@@ -6188,13 +6188,13 @@
|
|
|
6188
6188
|
},
|
|
6189
6189
|
{
|
|
6190
6190
|
"path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py",
|
|
6191
|
-
"bytes":
|
|
6192
|
-
"sha256": "
|
|
6191
|
+
"bytes": 344185,
|
|
6192
|
+
"sha256": "5fbb3018d4ac1764d711bca30f5d4ce1a0466015856a99c59fa4a1d25f732a7b"
|
|
6193
6193
|
},
|
|
6194
6194
|
{
|
|
6195
6195
|
"path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_flow.py",
|
|
6196
|
-
"bytes":
|
|
6197
|
-
"sha256": "
|
|
6196
|
+
"bytes": 335994,
|
|
6197
|
+
"sha256": "2a0e0c49c268e6d00cffbabc11f985a00b7a700adbe7bb6efa4f2da7c08c10fd"
|
|
6198
6198
|
},
|
|
6199
6199
|
{
|
|
6200
6200
|
"path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -204,6 +204,40 @@ def resolve_repo_relative_path(target_root: Path, relative: str, *, label: str)
|
|
|
204
204
|
return candidate, []
|
|
205
205
|
|
|
206
206
|
|
|
207
|
+
def path_boundary_missing_details(*, label: str, locator: object, errors: list[str]) -> list[dict[str, object]]:
|
|
208
|
+
"""Return stable machine-readable details for repo locator boundary failures."""
|
|
209
|
+
locator_text = "" if locator is None else str(locator)
|
|
210
|
+
details: list[dict[str, object]] = []
|
|
211
|
+
for message in errors:
|
|
212
|
+
if "non-empty" in message:
|
|
213
|
+
kind = "empty_locator"
|
|
214
|
+
elif "absolute path" in message or "repo-relative" in message:
|
|
215
|
+
kind = "absolute_locator"
|
|
216
|
+
else:
|
|
217
|
+
kind = "repo_locator_escape"
|
|
218
|
+
|
|
219
|
+
scopes: list[str] = []
|
|
220
|
+
if "target root" in message:
|
|
221
|
+
scopes.append("target_root")
|
|
222
|
+
if "repository" in message or "repo-relative" in message:
|
|
223
|
+
scopes.append("repository_root")
|
|
224
|
+
if not scopes:
|
|
225
|
+
scopes.append("repository_root")
|
|
226
|
+
|
|
227
|
+
for scope in scopes:
|
|
228
|
+
details.append(
|
|
229
|
+
{
|
|
230
|
+
"category": "path_boundary",
|
|
231
|
+
"kind": kind,
|
|
232
|
+
"scope": scope,
|
|
233
|
+
"label": label,
|
|
234
|
+
"locator": locator_text,
|
|
235
|
+
"message": message,
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
return details
|
|
239
|
+
|
|
240
|
+
|
|
207
241
|
def parse_work_item(path: Path, root: Path) -> tuple[dict[str, object], list[str]]:
|
|
208
242
|
relative_path = _relative(path, root)
|
|
209
243
|
sections = markdown_sections(path)
|
|
@@ -1062,6 +1062,26 @@ def require_repo_specific_requirements_payload(
|
|
|
1062
1062
|
failures.append(Failure(category, f"{context} declared requirements must split cleanly into blocking and advisory"))
|
|
1063
1063
|
|
|
1064
1064
|
|
|
1065
|
+
def require_missing_details(
|
|
1066
|
+
failures: list[Failure],
|
|
1067
|
+
*,
|
|
1068
|
+
category: str,
|
|
1069
|
+
context: str,
|
|
1070
|
+
details: object,
|
|
1071
|
+
) -> None:
|
|
1072
|
+
if not isinstance(details, list):
|
|
1073
|
+
failures.append(Failure(category, f"{context} must be a list"))
|
|
1074
|
+
return
|
|
1075
|
+
for index, detail in enumerate(details):
|
|
1076
|
+
if not isinstance(detail, dict):
|
|
1077
|
+
failures.append(Failure(category, f"{context}[{index}] must be an object"))
|
|
1078
|
+
continue
|
|
1079
|
+
for field in ("category", "kind", "scope", "label", "locator", "message"):
|
|
1080
|
+
value = detail.get(field)
|
|
1081
|
+
if not isinstance(value, str) or not value:
|
|
1082
|
+
failures.append(Failure(category, f"{context}[{index}] missing `{field}`"))
|
|
1083
|
+
|
|
1084
|
+
|
|
1065
1085
|
def require_shadow_parity_payload(
|
|
1066
1086
|
failures: list[Failure],
|
|
1067
1087
|
*,
|
|
@@ -1090,6 +1110,14 @@ def require_shadow_parity_payload(
|
|
|
1090
1110
|
failures.append(Failure(category, f"{context} must include non-empty `summary`"))
|
|
1091
1111
|
if not isinstance(payload.get("missing_inputs"), list):
|
|
1092
1112
|
failures.append(Failure(category, f"{context} must include `missing_inputs`"))
|
|
1113
|
+
top_level_details = payload.get("missing_details")
|
|
1114
|
+
if top_level_details is not None:
|
|
1115
|
+
require_missing_details(
|
|
1116
|
+
failures,
|
|
1117
|
+
category=category,
|
|
1118
|
+
context=f"{context}.missing_details",
|
|
1119
|
+
details=top_level_details,
|
|
1120
|
+
)
|
|
1093
1121
|
require_runtime_state_payload(
|
|
1094
1122
|
failures,
|
|
1095
1123
|
category=category,
|
|
@@ -1133,6 +1161,14 @@ def require_shadow_parity_payload(
|
|
|
1133
1161
|
failures.append(Failure(category, f"{context} reports[{index}] must include non-empty `summary`"))
|
|
1134
1162
|
if not isinstance(report.get("missing_inputs"), list):
|
|
1135
1163
|
failures.append(Failure(category, f"{context} reports[{index}] must include `missing_inputs`"))
|
|
1164
|
+
report_details = report.get("missing_details")
|
|
1165
|
+
if report_details is not None:
|
|
1166
|
+
require_missing_details(
|
|
1167
|
+
failures,
|
|
1168
|
+
category=category,
|
|
1169
|
+
context=f"{context} reports[{index}].missing_details",
|
|
1170
|
+
details=report_details,
|
|
1171
|
+
)
|
|
1136
1172
|
for key in ("host_adapters", "repo_native_carriers"):
|
|
1137
1173
|
if not isinstance(report.get(key), list):
|
|
1138
1174
|
failures.append(Failure(category, f"{context} reports[{index}] must include `{key}` as a list"))
|
|
@@ -6460,6 +6496,43 @@ def check_adversarial_adoption_fixture(root: Path) -> list[Failure]:
|
|
|
6460
6496
|
):
|
|
6461
6497
|
failures.append(Failure("adversarial-adoption", "shadow surface path-boundary errors must expose a stable repository anchor"))
|
|
6462
6498
|
|
|
6499
|
+
shadow_boundary_target = base / "shadow-structured-boundary"
|
|
6500
|
+
(shadow_boundary_target / ".loom/companion").mkdir(parents=True)
|
|
6501
|
+
write_json(
|
|
6502
|
+
shadow_boundary_target / ".loom/companion/interop.json",
|
|
6503
|
+
{
|
|
6504
|
+
"schema_version": "loom-repo-interop/v1",
|
|
6505
|
+
"host_adapters": [],
|
|
6506
|
+
"repo_native_carriers": [],
|
|
6507
|
+
"shadow_surfaces": {
|
|
6508
|
+
"review": {
|
|
6509
|
+
"summary": "review parity",
|
|
6510
|
+
"loom_locator": "../outside-loom.json",
|
|
6511
|
+
"repo_locator": "../outside-repo.json",
|
|
6512
|
+
}
|
|
6513
|
+
},
|
|
6514
|
+
},
|
|
6515
|
+
)
|
|
6516
|
+
shadow_boundary_report = loom_flow_module.shadow_parity_report(
|
|
6517
|
+
{
|
|
6518
|
+
"availability": "present",
|
|
6519
|
+
"contract": {"locator": ".loom/companion/interop.json"},
|
|
6520
|
+
},
|
|
6521
|
+
target_root=shadow_boundary_target,
|
|
6522
|
+
surface="review",
|
|
6523
|
+
)
|
|
6524
|
+
shadow_boundary_details = shadow_boundary_report.get("missing_details")
|
|
6525
|
+
if not isinstance(shadow_boundary_details, list) or not any(
|
|
6526
|
+
isinstance(detail, dict)
|
|
6527
|
+
and detail.get("category") == "path_boundary"
|
|
6528
|
+
and detail.get("kind") == "repo_locator_escape"
|
|
6529
|
+
and detail.get("scope") == "repository_root"
|
|
6530
|
+
and detail.get("label") == "shadow surface `review` repo_locator"
|
|
6531
|
+
and detail.get("locator") == "../outside-repo.json"
|
|
6532
|
+
for detail in shadow_boundary_details
|
|
6533
|
+
):
|
|
6534
|
+
failures.append(Failure("adversarial-adoption", "shadow parity path-boundary failures must expose structured missing_details"))
|
|
6535
|
+
|
|
6463
6536
|
spec_contract_target = base / "spec-contract"
|
|
6464
6537
|
(spec_contract_target / ".loom/specs/INIT-0001").mkdir(parents=True)
|
|
6465
6538
|
(spec_contract_target / ".loom/specs/INIT-0001/spec.md").write_text("bootstrap spec\n", encoding="utf-8")
|
|
@@ -6622,6 +6695,15 @@ def check_adversarial_adoption_fixture(root: Path) -> list[Failure]:
|
|
|
6622
6695
|
failures.append(Failure("adversarial-adoption", f"path escape shadow-parity sample failed: {error}"))
|
|
6623
6696
|
elif shadow_escape_payload.get("result") != "block":
|
|
6624
6697
|
failures.append(Failure("adversarial-adoption", "shadow-parity blocking mode must reject absolute shadow locators"))
|
|
6698
|
+
else:
|
|
6699
|
+
shadow_escape_details = shadow_escape_payload.get("missing_details")
|
|
6700
|
+
if not isinstance(shadow_escape_details, list) or not any(
|
|
6701
|
+
isinstance(detail, dict)
|
|
6702
|
+
and detail.get("category") == "path_boundary"
|
|
6703
|
+
and "loom_locator" in str(detail.get("label", ""))
|
|
6704
|
+
for detail in shadow_escape_details
|
|
6705
|
+
):
|
|
6706
|
+
failures.append(Failure("adversarial-adoption", "shadow-parity CLI payload must aggregate structured path-boundary missing_details"))
|
|
6625
6707
|
|
|
6626
6708
|
poisoned_payload, error = load_command_json(
|
|
6627
6709
|
root,
|
|
@@ -26,6 +26,7 @@ from fact_chain_support import (
|
|
|
26
26
|
parse_key_value_section,
|
|
27
27
|
parse_recovery_entry,
|
|
28
28
|
parse_work_item,
|
|
29
|
+
path_boundary_missing_details,
|
|
29
30
|
resolve_repo_relative_path,
|
|
30
31
|
)
|
|
31
32
|
from governance_surface import build_governance_surface
|
|
@@ -609,6 +610,17 @@ def repo_relative_path(target_root: Path, relative: str) -> Path | None:
|
|
|
609
610
|
return None if errors else candidate
|
|
610
611
|
|
|
611
612
|
|
|
613
|
+
def path_boundary_details_from_messages(errors: list[str]) -> list[dict[str, object]]:
|
|
614
|
+
details: list[dict[str, object]] = []
|
|
615
|
+
for message in errors:
|
|
616
|
+
if "must stay" not in message and "repo-relative" not in message and "non-empty repo-relative" not in message:
|
|
617
|
+
continue
|
|
618
|
+
label = message.split(" must ", 1)[0] if " must " in message else "repo locator"
|
|
619
|
+
locator = message.rsplit(": ", 1)[-1] if ": " in message else ""
|
|
620
|
+
details.extend(path_boundary_missing_details(label=label, locator=locator, errors=[message]))
|
|
621
|
+
return details
|
|
622
|
+
|
|
623
|
+
|
|
612
624
|
def validate_shadow_sources(payload: dict[str, Any], *, path: Path, target_root: Path) -> tuple[dict[str, Any], list[str]]:
|
|
613
625
|
source_files = payload.get("source_files")
|
|
614
626
|
source_sha256 = payload.get("source_sha256")
|
|
@@ -766,11 +778,17 @@ def shadow_parity_report(
|
|
|
766
778
|
}
|
|
767
779
|
interop_payload, interop_errors = load_repo_interop_contract(repo_interop, target_root=target_root)
|
|
768
780
|
if interop_errors:
|
|
769
|
-
|
|
781
|
+
missing_details = path_boundary_details_from_messages(interop_errors)
|
|
782
|
+
payload = {
|
|
770
783
|
**empty_report,
|
|
771
784
|
"summary": "shadow parity is unavailable because the repo interop contract is missing or incomplete.",
|
|
772
785
|
"missing_inputs": interop_errors,
|
|
773
786
|
}
|
|
787
|
+
if missing_details:
|
|
788
|
+
payload["missing_details"] = missing_details
|
|
789
|
+
return {
|
|
790
|
+
**payload,
|
|
791
|
+
}
|
|
774
792
|
if not isinstance(interop_payload, dict):
|
|
775
793
|
return empty_report
|
|
776
794
|
|
|
@@ -813,10 +831,23 @@ def shadow_parity_report(
|
|
|
813
831
|
label=f"shadow surface `{surface}` repo_locator",
|
|
814
832
|
)
|
|
815
833
|
if loom_locator_errors or repo_locator_errors:
|
|
834
|
+
missing_details = [
|
|
835
|
+
*path_boundary_missing_details(
|
|
836
|
+
label=f"shadow surface `{surface}` loom_locator",
|
|
837
|
+
locator=loom_locator,
|
|
838
|
+
errors=loom_locator_errors,
|
|
839
|
+
),
|
|
840
|
+
*path_boundary_missing_details(
|
|
841
|
+
label=f"shadow surface `{surface}` repo_locator",
|
|
842
|
+
locator=repo_locator,
|
|
843
|
+
errors=repo_locator_errors,
|
|
844
|
+
),
|
|
845
|
+
]
|
|
816
846
|
return {
|
|
817
847
|
**empty_report,
|
|
818
848
|
"summary": "shadow parity is unavailable because a declared surface locator is unsafe.",
|
|
819
849
|
"missing_inputs": [*loom_locator_errors, *repo_locator_errors],
|
|
850
|
+
"missing_details": missing_details,
|
|
820
851
|
"host_adapters": relevant_host_adapters,
|
|
821
852
|
"repo_native_carriers": relevant_repo_native_carriers,
|
|
822
853
|
}
|
|
@@ -7738,25 +7769,32 @@ def handle_shadow_parity(args: argparse.Namespace) -> int:
|
|
|
7738
7769
|
summary = "shadow parity could not fully read the declared governance surfaces."
|
|
7739
7770
|
|
|
7740
7771
|
missing_inputs: list[str] = []
|
|
7772
|
+
missing_details: list[Any] = []
|
|
7741
7773
|
for report in reports:
|
|
7742
7774
|
for message in report.get("missing_inputs", []):
|
|
7743
7775
|
if message not in missing_inputs:
|
|
7744
7776
|
missing_inputs.append(message)
|
|
7777
|
+
details = report.get("missing_details")
|
|
7778
|
+
if isinstance(details, list):
|
|
7779
|
+
for detail in details:
|
|
7780
|
+
if detail not in missing_details:
|
|
7781
|
+
missing_details.append(detail)
|
|
7745
7782
|
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7755
|
-
|
|
7756
|
-
|
|
7757
|
-
|
|
7758
|
-
|
|
7759
|
-
|
|
7783
|
+
payload = {
|
|
7784
|
+
"command": "shadow-parity",
|
|
7785
|
+
"mode": mode,
|
|
7786
|
+
"blocking": mode == "blocking",
|
|
7787
|
+
"result": result,
|
|
7788
|
+
"summary": summary,
|
|
7789
|
+
"missing_inputs": missing_inputs,
|
|
7790
|
+
"fallback_to": "manual-reconciliation" if result == "block" else None,
|
|
7791
|
+
"runtime_state": runtime_state,
|
|
7792
|
+
"governance_surface": governance_surface,
|
|
7793
|
+
"reports": reports,
|
|
7794
|
+
}
|
|
7795
|
+
if missing_details:
|
|
7796
|
+
payload["missing_details"] = missing_details
|
|
7797
|
+
return emit(payload)
|
|
7760
7798
|
|
|
7761
7799
|
|
|
7762
7800
|
def handle_runtime_parity(args: argparse.Namespace) -> int:
|
|
@@ -204,6 +204,40 @@ def resolve_repo_relative_path(target_root: Path, relative: str, *, label: str)
|
|
|
204
204
|
return candidate, []
|
|
205
205
|
|
|
206
206
|
|
|
207
|
+
def path_boundary_missing_details(*, label: str, locator: object, errors: list[str]) -> list[dict[str, object]]:
|
|
208
|
+
"""Return stable machine-readable details for repo locator boundary failures."""
|
|
209
|
+
locator_text = "" if locator is None else str(locator)
|
|
210
|
+
details: list[dict[str, object]] = []
|
|
211
|
+
for message in errors:
|
|
212
|
+
if "non-empty" in message:
|
|
213
|
+
kind = "empty_locator"
|
|
214
|
+
elif "absolute path" in message or "repo-relative" in message:
|
|
215
|
+
kind = "absolute_locator"
|
|
216
|
+
else:
|
|
217
|
+
kind = "repo_locator_escape"
|
|
218
|
+
|
|
219
|
+
scopes: list[str] = []
|
|
220
|
+
if "target root" in message:
|
|
221
|
+
scopes.append("target_root")
|
|
222
|
+
if "repository" in message or "repo-relative" in message:
|
|
223
|
+
scopes.append("repository_root")
|
|
224
|
+
if not scopes:
|
|
225
|
+
scopes.append("repository_root")
|
|
226
|
+
|
|
227
|
+
for scope in scopes:
|
|
228
|
+
details.append(
|
|
229
|
+
{
|
|
230
|
+
"category": "path_boundary",
|
|
231
|
+
"kind": kind,
|
|
232
|
+
"scope": scope,
|
|
233
|
+
"label": label,
|
|
234
|
+
"locator": locator_text,
|
|
235
|
+
"message": message,
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
return details
|
|
239
|
+
|
|
240
|
+
|
|
207
241
|
def parse_work_item(path: Path, root: Path) -> tuple[dict[str, object], list[str]]:
|
|
208
242
|
relative_path = _relative(path, root)
|
|
209
243
|
sections = markdown_sections(path)
|