@mc-and-his-agents/loom-installer 0.1.49 → 0.1.51
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 +42 -42
- package/payload/plugin/loom/skills/shared/scripts/loom_check.py +135 -1
- package/payload/plugin/loom/skills/shared/scripts/loom_flow.py +29 -7
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py +135 -1
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_flow.py +29 -7
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py +135 -1
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_flow.py +29 -7
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_check.py +135 -1
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_flow.py +29 -7
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py +135 -1
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_flow.py +29 -7
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py +135 -1
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_flow.py +29 -7
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py +135 -1
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_flow.py +29 -7
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py +135 -1
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_flow.py +29 -7
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_check.py +135 -1
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_flow.py +29 -7
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py +135 -1
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_flow.py +29 -7
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": "25dcc71b214c7243c36a46a126de9239c6a06c00",
|
|
6
6
|
"source_ref": "main",
|
|
7
|
-
"built_at": "2026-04-
|
|
7
|
+
"built_at": "2026-04-28T13:55:40+08:00",
|
|
8
8
|
"runtime": {
|
|
9
9
|
"python_minimum": "3.10",
|
|
10
10
|
"python_recommended": "3.11+"
|
|
@@ -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": 370455,
|
|
637
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
638
638
|
},
|
|
639
639
|
{
|
|
640
640
|
"path": "plugin/loom/skills/shared/scripts/loom_flow.py",
|
|
641
|
-
"bytes":
|
|
642
|
-
"sha256": "
|
|
641
|
+
"bytes": 343536,
|
|
642
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
643
643
|
},
|
|
644
644
|
{
|
|
645
645
|
"path": "plugin/loom/skills/shared/scripts/loom_init.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": 370455,
|
|
1227
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
1228
1228
|
},
|
|
1229
1229
|
{
|
|
1230
1230
|
"path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_flow.py",
|
|
1231
|
-
"bytes":
|
|
1232
|
-
"sha256": "
|
|
1231
|
+
"bytes": 343536,
|
|
1232
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
1233
1233
|
},
|
|
1234
1234
|
{
|
|
1235
1235
|
"path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_init.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": 370455,
|
|
1847
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
1848
1848
|
},
|
|
1849
1849
|
{
|
|
1850
1850
|
"path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_flow.py",
|
|
1851
|
-
"bytes":
|
|
1852
|
-
"sha256": "
|
|
1851
|
+
"bytes": 343536,
|
|
1852
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
1853
1853
|
},
|
|
1854
1854
|
{
|
|
1855
1855
|
"path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_init.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": 370455,
|
|
2467
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
2468
2468
|
},
|
|
2469
2469
|
{
|
|
2470
2470
|
"path": "skills/loom-init/.loom-runtime/shared/scripts/loom_flow.py",
|
|
2471
|
-
"bytes":
|
|
2472
|
-
"sha256": "
|
|
2471
|
+
"bytes": 343536,
|
|
2472
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
2473
2473
|
},
|
|
2474
2474
|
{
|
|
2475
2475
|
"path": "skills/loom-init/.loom-runtime/shared/scripts/loom_init.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": 370455,
|
|
3092
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
3093
3093
|
},
|
|
3094
3094
|
{
|
|
3095
3095
|
"path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_flow.py",
|
|
3096
|
-
"bytes":
|
|
3097
|
-
"sha256": "
|
|
3096
|
+
"bytes": 343536,
|
|
3097
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
3098
3098
|
},
|
|
3099
3099
|
{
|
|
3100
3100
|
"path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_init.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": 370455,
|
|
3712
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
3713
3713
|
},
|
|
3714
3714
|
{
|
|
3715
3715
|
"path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_flow.py",
|
|
3716
|
-
"bytes":
|
|
3717
|
-
"sha256": "
|
|
3716
|
+
"bytes": 343536,
|
|
3717
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
3718
3718
|
},
|
|
3719
3719
|
{
|
|
3720
3720
|
"path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_init.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": 370455,
|
|
4332
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
4333
4333
|
},
|
|
4334
4334
|
{
|
|
4335
4335
|
"path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_flow.py",
|
|
4336
|
-
"bytes":
|
|
4337
|
-
"sha256": "
|
|
4336
|
+
"bytes": 343536,
|
|
4337
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
4338
4338
|
},
|
|
4339
4339
|
{
|
|
4340
4340
|
"path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_init.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": 370455,
|
|
4952
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
4953
4953
|
},
|
|
4954
4954
|
{
|
|
4955
4955
|
"path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_flow.py",
|
|
4956
|
-
"bytes":
|
|
4957
|
-
"sha256": "
|
|
4956
|
+
"bytes": 343536,
|
|
4957
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
4958
4958
|
},
|
|
4959
4959
|
{
|
|
4960
4960
|
"path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_init.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": 370455,
|
|
5572
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
5573
5573
|
},
|
|
5574
5574
|
{
|
|
5575
5575
|
"path": "skills/loom-review/.loom-runtime/shared/scripts/loom_flow.py",
|
|
5576
|
-
"bytes":
|
|
5577
|
-
"sha256": "
|
|
5576
|
+
"bytes": 343536,
|
|
5577
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
5578
5578
|
},
|
|
5579
5579
|
{
|
|
5580
5580
|
"path": "skills/loom-review/.loom-runtime/shared/scripts/loom_init.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": 370455,
|
|
6192
|
+
"sha256": "1d60fbb276d1ba43c22823adbd6c570fde5730e23e61167f26c815b729fb0cc8"
|
|
6193
6193
|
},
|
|
6194
6194
|
{
|
|
6195
6195
|
"path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_flow.py",
|
|
6196
|
-
"bytes":
|
|
6197
|
-
"sha256": "
|
|
6196
|
+
"bytes": 343536,
|
|
6197
|
+
"sha256": "f8fe63875c5abce92281c4fce0fa329c3584a25a85c131ba2d0364ac39b78997"
|
|
6198
6198
|
},
|
|
6199
6199
|
{
|
|
6200
6200
|
"path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -2410,6 +2410,103 @@ def check_demo_repo_local_cli(root: Path) -> list[Failure]:
|
|
|
2410
2410
|
return failures
|
|
2411
2411
|
|
|
2412
2412
|
|
|
2413
|
+
def check_root_self_adoption_carrier(root: Path) -> list[Failure]:
|
|
2414
|
+
failures: list[Failure] = []
|
|
2415
|
+
carrier_root = root / ".loom"
|
|
2416
|
+
if not carrier_root.exists():
|
|
2417
|
+
return failures
|
|
2418
|
+
|
|
2419
|
+
required_paths = (
|
|
2420
|
+
".loom/bootstrap/manifest.json",
|
|
2421
|
+
".loom/bootstrap/init-result.json",
|
|
2422
|
+
".loom/bin/loom_init.py",
|
|
2423
|
+
".loom/bin/loom_flow.py",
|
|
2424
|
+
".loom/work-items/INIT-0001.md",
|
|
2425
|
+
".loom/progress/INIT-0001.md",
|
|
2426
|
+
".loom/reviews/INIT-0001.json",
|
|
2427
|
+
".loom/status/current.md",
|
|
2428
|
+
)
|
|
2429
|
+
failures.extend(check_required_paths(root, "root-self-adoption", required_paths))
|
|
2430
|
+
|
|
2431
|
+
commands = (
|
|
2432
|
+
(
|
|
2433
|
+
"root verify",
|
|
2434
|
+
["python3", ".loom/bin/loom_init.py", "verify", "--target", "."],
|
|
2435
|
+
"loom-init-verify",
|
|
2436
|
+
{"ok": True},
|
|
2437
|
+
),
|
|
2438
|
+
(
|
|
2439
|
+
"root governance status",
|
|
2440
|
+
["python3", ".loom/bin/loom_flow.py", "governance-profile", "status", "--target", "."],
|
|
2441
|
+
"governance-status",
|
|
2442
|
+
{"result": "pass"},
|
|
2443
|
+
),
|
|
2444
|
+
(
|
|
2445
|
+
"root runtime parity",
|
|
2446
|
+
["python3", ".loom/bin/loom_flow.py", "runtime-parity", "validate", "--target", "."],
|
|
2447
|
+
"runtime-parity",
|
|
2448
|
+
{"result": "pass", "schema_version": "loom-runtime-parity/v1"},
|
|
2449
|
+
),
|
|
2450
|
+
(
|
|
2451
|
+
"root adopt verify",
|
|
2452
|
+
["python3", ".loom/bin/loom_flow.py", "adopt", "verify", "--target", ".", "--item", "INIT-0001"],
|
|
2453
|
+
"adopt-verify",
|
|
2454
|
+
{"result": "pass", "schema_version": "loom-adoption-verify/v1"},
|
|
2455
|
+
),
|
|
2456
|
+
(
|
|
2457
|
+
"root carrier refresh",
|
|
2458
|
+
["python3", ".loom/bin/loom_flow.py", "carrier", "refresh", "--target", ".", "--dry-run"],
|
|
2459
|
+
"carrier-refresh",
|
|
2460
|
+
{"result": "pass", "schema_version": "loom-carrier-refresh/v1"},
|
|
2461
|
+
),
|
|
2462
|
+
)
|
|
2463
|
+
for label, args, kind, expected in commands:
|
|
2464
|
+
payload, error = load_command_json(root, args)
|
|
2465
|
+
if error:
|
|
2466
|
+
failures.append(Failure("root-self-adoption", f"`{label}` failed: {error}"))
|
|
2467
|
+
continue
|
|
2468
|
+
for key, value in expected.items():
|
|
2469
|
+
if payload.get(key) != value:
|
|
2470
|
+
failures.append(Failure("root-self-adoption", f"`{label}` must report `{key}: {value}`"))
|
|
2471
|
+
runtime_payload = payload.get("runtime_state")
|
|
2472
|
+
if kind == "loom-init-verify":
|
|
2473
|
+
runtime_payload = payload.get("runtime_state")
|
|
2474
|
+
if runtime_payload is not None:
|
|
2475
|
+
require_runtime_state_payload(
|
|
2476
|
+
failures,
|
|
2477
|
+
category="root-self-adoption",
|
|
2478
|
+
context=f"`{label}`",
|
|
2479
|
+
payload=runtime_payload,
|
|
2480
|
+
expected_scene="installed-runtime",
|
|
2481
|
+
expected_carrier="bootstrapped-target-runtime",
|
|
2482
|
+
allowed_results={"pass"},
|
|
2483
|
+
)
|
|
2484
|
+
if kind == "governance-status":
|
|
2485
|
+
maturity = payload.get("maturity")
|
|
2486
|
+
if not isinstance(maturity, dict) or maturity.get("current") not in {"light", "standard", "strong"}:
|
|
2487
|
+
failures.append(Failure("root-self-adoption", "`root governance status` must report adopted maturity"))
|
|
2488
|
+
if kind == "runtime-parity":
|
|
2489
|
+
checks = payload.get("checks")
|
|
2490
|
+
names = {check.get("name") for check in checks if isinstance(check, dict)} if isinstance(checks, list) else set()
|
|
2491
|
+
if "shadow_parity_boundary" not in names or "controlled_merge_contract" not in names:
|
|
2492
|
+
failures.append(Failure("root-self-adoption", "`root runtime parity` must cover shadow and controlled merge boundaries"))
|
|
2493
|
+
if kind == "adopt-verify":
|
|
2494
|
+
roundtrip = payload.get("producer_consumer_roundtrip")
|
|
2495
|
+
if not isinstance(roundtrip, dict):
|
|
2496
|
+
failures.append(Failure("root-self-adoption", "`root adopt verify` must include producer_consumer_roundtrip"))
|
|
2497
|
+
elif roundtrip.get("bypass_check", {}).get("result") != "pass":
|
|
2498
|
+
failures.append(Failure("root-self-adoption", "`root adopt verify` must prove required section deletion blocks"))
|
|
2499
|
+
if kind == "carrier-refresh":
|
|
2500
|
+
if payload.get("refresh_needed") not in ([], None):
|
|
2501
|
+
failures.append(Failure("root-self-adoption", "`root carrier refresh --dry-run` must not report runtime provenance drift"))
|
|
2502
|
+
review = payload.get("review")
|
|
2503
|
+
if isinstance(review, dict):
|
|
2504
|
+
head_binding = review.get("head_binding")
|
|
2505
|
+
if isinstance(head_binding, dict) and head_binding.get("status") == "stale":
|
|
2506
|
+
failures.append(Failure("root-self-adoption", "`root carrier refresh --dry-run` must detect stale review head binding"))
|
|
2507
|
+
return failures
|
|
2508
|
+
|
|
2509
|
+
|
|
2413
2510
|
def check_deep_existing_repo_bootstrap(root: Path) -> list[Failure]:
|
|
2414
2511
|
failures: list[Failure] = []
|
|
2415
2512
|
with tempfile.TemporaryDirectory(prefix="loom-check-deep-existing-") as tmp:
|
|
@@ -4466,6 +4563,42 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
|
|
|
4466
4563
|
elif payload.get("result") != "pass":
|
|
4467
4564
|
failures.append(Failure("daily-execution-cli", "`installed spec review record allow` must pass"))
|
|
4468
4565
|
|
|
4566
|
+
spec_plan_path = positive_target / ".loom/specs/INIT-0001/plan.md"
|
|
4567
|
+
spec_plan_text = spec_plan_path.read_text(encoding="utf-8")
|
|
4568
|
+
spec_plan_path.unlink()
|
|
4569
|
+
try:
|
|
4570
|
+
payload, error = load_command_json(
|
|
4571
|
+
root,
|
|
4572
|
+
[
|
|
4573
|
+
"python3",
|
|
4574
|
+
str(install_root / "loom-spec-review" / "scripts" / "loom-spec-review.py"),
|
|
4575
|
+
"review",
|
|
4576
|
+
"record",
|
|
4577
|
+
"--target",
|
|
4578
|
+
str(positive_target),
|
|
4579
|
+
"--item",
|
|
4580
|
+
"INIT-0001",
|
|
4581
|
+
"--review-file",
|
|
4582
|
+
".loom/reviews/INIT-0001.spec.json",
|
|
4583
|
+
"--decision",
|
|
4584
|
+
"allow",
|
|
4585
|
+
"--kind",
|
|
4586
|
+
"spec_review",
|
|
4587
|
+
"--summary",
|
|
4588
|
+
"Incomplete formal spec suite should not be approved.",
|
|
4589
|
+
"--reviewer",
|
|
4590
|
+
"loom-check",
|
|
4591
|
+
],
|
|
4592
|
+
)
|
|
4593
|
+
if error:
|
|
4594
|
+
failures.append(Failure("daily-execution-cli", f"`installed incomplete spec review record` failed: {error}"))
|
|
4595
|
+
elif payload.get("result") != "block":
|
|
4596
|
+
failures.append(Failure("daily-execution-cli", "`installed incomplete spec review record` must block"))
|
|
4597
|
+
elif not any("plan.md" in str(item) for item in payload.get("missing_inputs", [])):
|
|
4598
|
+
failures.append(Failure("daily-execution-cli", "`installed incomplete spec review record` must name the missing plan.md"))
|
|
4599
|
+
finally:
|
|
4600
|
+
spec_plan_path.write_text(spec_plan_text, encoding="utf-8")
|
|
4601
|
+
|
|
4469
4602
|
payload, error = load_command_json(
|
|
4470
4603
|
root,
|
|
4471
4604
|
[
|
|
@@ -7413,6 +7546,7 @@ def collect_failures(root: Path) -> list[Failure]:
|
|
|
7413
7546
|
failures.extend(check_demo_assets(root))
|
|
7414
7547
|
failures.extend(check_demo_fact_chain(root))
|
|
7415
7548
|
failures.extend(check_demo_repo_local_cli(root))
|
|
7549
|
+
failures.extend(check_root_self_adoption_carrier(root))
|
|
7416
7550
|
failures.extend(check_deep_existing_repo_bootstrap(root))
|
|
7417
7551
|
failures.extend(check_daily_execution_cli(root))
|
|
7418
7552
|
failures.extend(check_repo_companion_interface_contracts(root))
|
|
@@ -7427,7 +7561,7 @@ def collect_failures(root: Path) -> list[Failure]:
|
|
|
7427
7561
|
|
|
7428
7562
|
|
|
7429
7563
|
def print_report(root: Path, failures: list[Failure]) -> None:
|
|
7430
|
-
categories_checked =
|
|
7564
|
+
categories_checked = 19
|
|
7431
7565
|
if not failures:
|
|
7432
7566
|
print(f"loom_check: OK ({root})")
|
|
7433
7567
|
print(f"checked {categories_checked} surfaces")
|
|
@@ -1541,6 +1541,16 @@ def formal_spec_path(context: dict[str, Any]) -> str | None:
|
|
|
1541
1541
|
return None
|
|
1542
1542
|
|
|
1543
1543
|
|
|
1544
|
+
def formal_spec_suite_status(context: dict[str, Any]) -> tuple[dict[str, str], list[str]]:
|
|
1545
|
+
suite = spec_suite_paths(context)
|
|
1546
|
+
missing = [
|
|
1547
|
+
path
|
|
1548
|
+
for path in (suite["spec"], suite["plan"], suite["implementation_contract"])
|
|
1549
|
+
if not (context["target_root"] / path).is_file()
|
|
1550
|
+
]
|
|
1551
|
+
return suite, missing
|
|
1552
|
+
|
|
1553
|
+
|
|
1544
1554
|
def spec_suite_paths(context: dict[str, Any]) -> dict[str, str]:
|
|
1545
1555
|
item_id = context["item_id"]
|
|
1546
1556
|
candidates = [
|
|
@@ -1757,15 +1767,25 @@ def review_gate_payload(
|
|
|
1757
1767
|
|
|
1758
1768
|
|
|
1759
1769
|
def spec_review_gate_payload(context: dict[str, Any]) -> dict[str, Any]:
|
|
1760
|
-
|
|
1761
|
-
|
|
1770
|
+
suite, missing_suite_paths = formal_spec_suite_status(context)
|
|
1771
|
+
spec_path = suite["spec"] if not missing_suite_paths else formal_spec_path(context)
|
|
1772
|
+
payload = review_gate_payload(
|
|
1762
1773
|
context,
|
|
1763
1774
|
review_path=default_spec_review_path(context["item_id"]),
|
|
1764
1775
|
expected_kind="spec_review",
|
|
1765
1776
|
gate_name="spec_review",
|
|
1766
|
-
required=
|
|
1777
|
+
required=not missing_suite_paths,
|
|
1767
1778
|
path_label=spec_path,
|
|
1768
1779
|
)
|
|
1780
|
+
payload["formal_spec_suite"] = suite
|
|
1781
|
+
if missing_suite_paths:
|
|
1782
|
+
payload["result"] = "block"
|
|
1783
|
+
payload["summary"] = "spec review is blocked until the complete formal spec suite is present."
|
|
1784
|
+
payload["missing_inputs"] = [
|
|
1785
|
+
f"missing formal spec suite file: {path}" for path in missing_suite_paths
|
|
1786
|
+
] + list(payload.get("missing_inputs", []))
|
|
1787
|
+
payload["fallback_to"] = "build"
|
|
1788
|
+
return payload
|
|
1769
1789
|
|
|
1770
1790
|
|
|
1771
1791
|
def implementation_review_status_payload(context: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -6813,15 +6833,17 @@ def handle_review(args: argparse.Namespace) -> int:
|
|
|
6813
6833
|
}
|
|
6814
6834
|
)
|
|
6815
6835
|
if args.decision == "allow" and args.kind == "spec_review":
|
|
6816
|
-
|
|
6817
|
-
if
|
|
6836
|
+
_, missing_suite_paths = formal_spec_suite_status(context)
|
|
6837
|
+
if missing_suite_paths:
|
|
6818
6838
|
return emit(
|
|
6819
6839
|
{
|
|
6820
6840
|
"command": "review",
|
|
6821
6841
|
"operation": "record",
|
|
6822
6842
|
"result": "block",
|
|
6823
|
-
"summary": "spec review cannot be recorded as `allow` without
|
|
6824
|
-
"missing_inputs": [
|
|
6843
|
+
"summary": "spec review cannot be recorded as `allow` without the complete formal spec suite.",
|
|
6844
|
+
"missing_inputs": [
|
|
6845
|
+
f"missing formal spec suite file: {path}" for path in missing_suite_paths
|
|
6846
|
+
],
|
|
6825
6847
|
"fallback_to": "build",
|
|
6826
6848
|
"build_checkpoint": build_payload,
|
|
6827
6849
|
}
|
|
@@ -2410,6 +2410,103 @@ def check_demo_repo_local_cli(root: Path) -> list[Failure]:
|
|
|
2410
2410
|
return failures
|
|
2411
2411
|
|
|
2412
2412
|
|
|
2413
|
+
def check_root_self_adoption_carrier(root: Path) -> list[Failure]:
|
|
2414
|
+
failures: list[Failure] = []
|
|
2415
|
+
carrier_root = root / ".loom"
|
|
2416
|
+
if not carrier_root.exists():
|
|
2417
|
+
return failures
|
|
2418
|
+
|
|
2419
|
+
required_paths = (
|
|
2420
|
+
".loom/bootstrap/manifest.json",
|
|
2421
|
+
".loom/bootstrap/init-result.json",
|
|
2422
|
+
".loom/bin/loom_init.py",
|
|
2423
|
+
".loom/bin/loom_flow.py",
|
|
2424
|
+
".loom/work-items/INIT-0001.md",
|
|
2425
|
+
".loom/progress/INIT-0001.md",
|
|
2426
|
+
".loom/reviews/INIT-0001.json",
|
|
2427
|
+
".loom/status/current.md",
|
|
2428
|
+
)
|
|
2429
|
+
failures.extend(check_required_paths(root, "root-self-adoption", required_paths))
|
|
2430
|
+
|
|
2431
|
+
commands = (
|
|
2432
|
+
(
|
|
2433
|
+
"root verify",
|
|
2434
|
+
["python3", ".loom/bin/loom_init.py", "verify", "--target", "."],
|
|
2435
|
+
"loom-init-verify",
|
|
2436
|
+
{"ok": True},
|
|
2437
|
+
),
|
|
2438
|
+
(
|
|
2439
|
+
"root governance status",
|
|
2440
|
+
["python3", ".loom/bin/loom_flow.py", "governance-profile", "status", "--target", "."],
|
|
2441
|
+
"governance-status",
|
|
2442
|
+
{"result": "pass"},
|
|
2443
|
+
),
|
|
2444
|
+
(
|
|
2445
|
+
"root runtime parity",
|
|
2446
|
+
["python3", ".loom/bin/loom_flow.py", "runtime-parity", "validate", "--target", "."],
|
|
2447
|
+
"runtime-parity",
|
|
2448
|
+
{"result": "pass", "schema_version": "loom-runtime-parity/v1"},
|
|
2449
|
+
),
|
|
2450
|
+
(
|
|
2451
|
+
"root adopt verify",
|
|
2452
|
+
["python3", ".loom/bin/loom_flow.py", "adopt", "verify", "--target", ".", "--item", "INIT-0001"],
|
|
2453
|
+
"adopt-verify",
|
|
2454
|
+
{"result": "pass", "schema_version": "loom-adoption-verify/v1"},
|
|
2455
|
+
),
|
|
2456
|
+
(
|
|
2457
|
+
"root carrier refresh",
|
|
2458
|
+
["python3", ".loom/bin/loom_flow.py", "carrier", "refresh", "--target", ".", "--dry-run"],
|
|
2459
|
+
"carrier-refresh",
|
|
2460
|
+
{"result": "pass", "schema_version": "loom-carrier-refresh/v1"},
|
|
2461
|
+
),
|
|
2462
|
+
)
|
|
2463
|
+
for label, args, kind, expected in commands:
|
|
2464
|
+
payload, error = load_command_json(root, args)
|
|
2465
|
+
if error:
|
|
2466
|
+
failures.append(Failure("root-self-adoption", f"`{label}` failed: {error}"))
|
|
2467
|
+
continue
|
|
2468
|
+
for key, value in expected.items():
|
|
2469
|
+
if payload.get(key) != value:
|
|
2470
|
+
failures.append(Failure("root-self-adoption", f"`{label}` must report `{key}: {value}`"))
|
|
2471
|
+
runtime_payload = payload.get("runtime_state")
|
|
2472
|
+
if kind == "loom-init-verify":
|
|
2473
|
+
runtime_payload = payload.get("runtime_state")
|
|
2474
|
+
if runtime_payload is not None:
|
|
2475
|
+
require_runtime_state_payload(
|
|
2476
|
+
failures,
|
|
2477
|
+
category="root-self-adoption",
|
|
2478
|
+
context=f"`{label}`",
|
|
2479
|
+
payload=runtime_payload,
|
|
2480
|
+
expected_scene="installed-runtime",
|
|
2481
|
+
expected_carrier="bootstrapped-target-runtime",
|
|
2482
|
+
allowed_results={"pass"},
|
|
2483
|
+
)
|
|
2484
|
+
if kind == "governance-status":
|
|
2485
|
+
maturity = payload.get("maturity")
|
|
2486
|
+
if not isinstance(maturity, dict) or maturity.get("current") not in {"light", "standard", "strong"}:
|
|
2487
|
+
failures.append(Failure("root-self-adoption", "`root governance status` must report adopted maturity"))
|
|
2488
|
+
if kind == "runtime-parity":
|
|
2489
|
+
checks = payload.get("checks")
|
|
2490
|
+
names = {check.get("name") for check in checks if isinstance(check, dict)} if isinstance(checks, list) else set()
|
|
2491
|
+
if "shadow_parity_boundary" not in names or "controlled_merge_contract" not in names:
|
|
2492
|
+
failures.append(Failure("root-self-adoption", "`root runtime parity` must cover shadow and controlled merge boundaries"))
|
|
2493
|
+
if kind == "adopt-verify":
|
|
2494
|
+
roundtrip = payload.get("producer_consumer_roundtrip")
|
|
2495
|
+
if not isinstance(roundtrip, dict):
|
|
2496
|
+
failures.append(Failure("root-self-adoption", "`root adopt verify` must include producer_consumer_roundtrip"))
|
|
2497
|
+
elif roundtrip.get("bypass_check", {}).get("result") != "pass":
|
|
2498
|
+
failures.append(Failure("root-self-adoption", "`root adopt verify` must prove required section deletion blocks"))
|
|
2499
|
+
if kind == "carrier-refresh":
|
|
2500
|
+
if payload.get("refresh_needed") not in ([], None):
|
|
2501
|
+
failures.append(Failure("root-self-adoption", "`root carrier refresh --dry-run` must not report runtime provenance drift"))
|
|
2502
|
+
review = payload.get("review")
|
|
2503
|
+
if isinstance(review, dict):
|
|
2504
|
+
head_binding = review.get("head_binding")
|
|
2505
|
+
if isinstance(head_binding, dict) and head_binding.get("status") == "stale":
|
|
2506
|
+
failures.append(Failure("root-self-adoption", "`root carrier refresh --dry-run` must detect stale review head binding"))
|
|
2507
|
+
return failures
|
|
2508
|
+
|
|
2509
|
+
|
|
2413
2510
|
def check_deep_existing_repo_bootstrap(root: Path) -> list[Failure]:
|
|
2414
2511
|
failures: list[Failure] = []
|
|
2415
2512
|
with tempfile.TemporaryDirectory(prefix="loom-check-deep-existing-") as tmp:
|
|
@@ -4466,6 +4563,42 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
|
|
|
4466
4563
|
elif payload.get("result") != "pass":
|
|
4467
4564
|
failures.append(Failure("daily-execution-cli", "`installed spec review record allow` must pass"))
|
|
4468
4565
|
|
|
4566
|
+
spec_plan_path = positive_target / ".loom/specs/INIT-0001/plan.md"
|
|
4567
|
+
spec_plan_text = spec_plan_path.read_text(encoding="utf-8")
|
|
4568
|
+
spec_plan_path.unlink()
|
|
4569
|
+
try:
|
|
4570
|
+
payload, error = load_command_json(
|
|
4571
|
+
root,
|
|
4572
|
+
[
|
|
4573
|
+
"python3",
|
|
4574
|
+
str(install_root / "loom-spec-review" / "scripts" / "loom-spec-review.py"),
|
|
4575
|
+
"review",
|
|
4576
|
+
"record",
|
|
4577
|
+
"--target",
|
|
4578
|
+
str(positive_target),
|
|
4579
|
+
"--item",
|
|
4580
|
+
"INIT-0001",
|
|
4581
|
+
"--review-file",
|
|
4582
|
+
".loom/reviews/INIT-0001.spec.json",
|
|
4583
|
+
"--decision",
|
|
4584
|
+
"allow",
|
|
4585
|
+
"--kind",
|
|
4586
|
+
"spec_review",
|
|
4587
|
+
"--summary",
|
|
4588
|
+
"Incomplete formal spec suite should not be approved.",
|
|
4589
|
+
"--reviewer",
|
|
4590
|
+
"loom-check",
|
|
4591
|
+
],
|
|
4592
|
+
)
|
|
4593
|
+
if error:
|
|
4594
|
+
failures.append(Failure("daily-execution-cli", f"`installed incomplete spec review record` failed: {error}"))
|
|
4595
|
+
elif payload.get("result") != "block":
|
|
4596
|
+
failures.append(Failure("daily-execution-cli", "`installed incomplete spec review record` must block"))
|
|
4597
|
+
elif not any("plan.md" in str(item) for item in payload.get("missing_inputs", [])):
|
|
4598
|
+
failures.append(Failure("daily-execution-cli", "`installed incomplete spec review record` must name the missing plan.md"))
|
|
4599
|
+
finally:
|
|
4600
|
+
spec_plan_path.write_text(spec_plan_text, encoding="utf-8")
|
|
4601
|
+
|
|
4469
4602
|
payload, error = load_command_json(
|
|
4470
4603
|
root,
|
|
4471
4604
|
[
|
|
@@ -7413,6 +7546,7 @@ def collect_failures(root: Path) -> list[Failure]:
|
|
|
7413
7546
|
failures.extend(check_demo_assets(root))
|
|
7414
7547
|
failures.extend(check_demo_fact_chain(root))
|
|
7415
7548
|
failures.extend(check_demo_repo_local_cli(root))
|
|
7549
|
+
failures.extend(check_root_self_adoption_carrier(root))
|
|
7416
7550
|
failures.extend(check_deep_existing_repo_bootstrap(root))
|
|
7417
7551
|
failures.extend(check_daily_execution_cli(root))
|
|
7418
7552
|
failures.extend(check_repo_companion_interface_contracts(root))
|
|
@@ -7427,7 +7561,7 @@ def collect_failures(root: Path) -> list[Failure]:
|
|
|
7427
7561
|
|
|
7428
7562
|
|
|
7429
7563
|
def print_report(root: Path, failures: list[Failure]) -> None:
|
|
7430
|
-
categories_checked =
|
|
7564
|
+
categories_checked = 19
|
|
7431
7565
|
if not failures:
|
|
7432
7566
|
print(f"loom_check: OK ({root})")
|
|
7433
7567
|
print(f"checked {categories_checked} surfaces")
|