@mc-and-his-agents/loom-installer 0.1.25 → 0.1.26

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.
Files changed (22) hide show
  1. package/package.json +1 -1
  2. package/payload/manifest.json +42 -42
  3. package/payload/plugin/loom/skills/shared/scripts/loom_check.py +71 -1
  4. package/payload/plugin/loom/skills/shared/scripts/loom_flow.py +7 -2
  5. package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py +71 -1
  6. package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_flow.py +7 -2
  7. package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py +71 -1
  8. package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_flow.py +7 -2
  9. package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_check.py +71 -1
  10. package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_flow.py +7 -2
  11. package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py +71 -1
  12. package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_flow.py +7 -2
  13. package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py +71 -1
  14. package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_flow.py +7 -2
  15. package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py +71 -1
  16. package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_flow.py +7 -2
  17. package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py +71 -1
  18. package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_flow.py +7 -2
  19. package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_check.py +71 -1
  20. package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_flow.py +7 -2
  21. package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py +71 -1
  22. package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_flow.py +7 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mc-and-his-agents/loom-installer",
3
- "version": "0.1.25",
3
+ "version": "0.1.26",
4
4
  "description": "Node installer for Loom plugin and single-skill installation surfaces.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -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": "b3b72c7a74674319f0a7432487fc0e3cbf012b4f",
5
+ "source_commit": "39faece767c89ee4de3f9388911433523241af6d",
6
6
  "source_ref": "main",
7
- "built_at": "2026-04-26T23:56:30+08:00",
7
+ "built_at": "2026-04-27T00:09:32+08:00",
8
8
  "runtime": {
9
9
  "python_minimum": "3.10",
10
10
  "python_recommended": "3.11+"
@@ -628,13 +628,13 @@
628
628
  },
629
629
  {
630
630
  "path": "plugin/loom/skills/shared/scripts/loom_check.py",
631
- "bytes": 287428,
632
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
631
+ "bytes": 291476,
632
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
633
633
  },
634
634
  {
635
635
  "path": "plugin/loom/skills/shared/scripts/loom_flow.py",
636
- "bytes": 296151,
637
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
636
+ "bytes": 296391,
637
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
638
638
  },
639
639
  {
640
640
  "path": "plugin/loom/skills/shared/scripts/loom_init.py",
@@ -1213,13 +1213,13 @@
1213
1213
  },
1214
1214
  {
1215
1215
  "path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py",
1216
- "bytes": 287428,
1217
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
1216
+ "bytes": 291476,
1217
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
1218
1218
  },
1219
1219
  {
1220
1220
  "path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_flow.py",
1221
- "bytes": 296151,
1222
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
1221
+ "bytes": 296391,
1222
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
1223
1223
  },
1224
1224
  {
1225
1225
  "path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_init.py",
@@ -1828,13 +1828,13 @@
1828
1828
  },
1829
1829
  {
1830
1830
  "path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py",
1831
- "bytes": 287428,
1832
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
1831
+ "bytes": 291476,
1832
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
1833
1833
  },
1834
1834
  {
1835
1835
  "path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_flow.py",
1836
- "bytes": 296151,
1837
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
1836
+ "bytes": 296391,
1837
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
1838
1838
  },
1839
1839
  {
1840
1840
  "path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_init.py",
@@ -2443,13 +2443,13 @@
2443
2443
  },
2444
2444
  {
2445
2445
  "path": "skills/loom-init/.loom-runtime/shared/scripts/loom_check.py",
2446
- "bytes": 287428,
2447
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
2446
+ "bytes": 291476,
2447
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
2448
2448
  },
2449
2449
  {
2450
2450
  "path": "skills/loom-init/.loom-runtime/shared/scripts/loom_flow.py",
2451
- "bytes": 296151,
2452
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
2451
+ "bytes": 296391,
2452
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
2453
2453
  },
2454
2454
  {
2455
2455
  "path": "skills/loom-init/.loom-runtime/shared/scripts/loom_init.py",
@@ -3063,13 +3063,13 @@
3063
3063
  },
3064
3064
  {
3065
3065
  "path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py",
3066
- "bytes": 287428,
3067
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
3066
+ "bytes": 291476,
3067
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
3068
3068
  },
3069
3069
  {
3070
3070
  "path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_flow.py",
3071
- "bytes": 296151,
3072
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
3071
+ "bytes": 296391,
3072
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
3073
3073
  },
3074
3074
  {
3075
3075
  "path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_init.py",
@@ -3678,13 +3678,13 @@
3678
3678
  },
3679
3679
  {
3680
3680
  "path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py",
3681
- "bytes": 287428,
3682
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
3681
+ "bytes": 291476,
3682
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
3683
3683
  },
3684
3684
  {
3685
3685
  "path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_flow.py",
3686
- "bytes": 296151,
3687
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
3686
+ "bytes": 296391,
3687
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
3688
3688
  },
3689
3689
  {
3690
3690
  "path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_init.py",
@@ -4293,13 +4293,13 @@
4293
4293
  },
4294
4294
  {
4295
4295
  "path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py",
4296
- "bytes": 287428,
4297
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
4296
+ "bytes": 291476,
4297
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
4298
4298
  },
4299
4299
  {
4300
4300
  "path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_flow.py",
4301
- "bytes": 296151,
4302
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
4301
+ "bytes": 296391,
4302
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
4303
4303
  },
4304
4304
  {
4305
4305
  "path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_init.py",
@@ -4908,13 +4908,13 @@
4908
4908
  },
4909
4909
  {
4910
4910
  "path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py",
4911
- "bytes": 287428,
4912
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
4911
+ "bytes": 291476,
4912
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
4913
4913
  },
4914
4914
  {
4915
4915
  "path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_flow.py",
4916
- "bytes": 296151,
4917
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
4916
+ "bytes": 296391,
4917
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
4918
4918
  },
4919
4919
  {
4920
4920
  "path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_init.py",
@@ -5523,13 +5523,13 @@
5523
5523
  },
5524
5524
  {
5525
5525
  "path": "skills/loom-review/.loom-runtime/shared/scripts/loom_check.py",
5526
- "bytes": 287428,
5527
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
5526
+ "bytes": 291476,
5527
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
5528
5528
  },
5529
5529
  {
5530
5530
  "path": "skills/loom-review/.loom-runtime/shared/scripts/loom_flow.py",
5531
- "bytes": 296151,
5532
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
5531
+ "bytes": 296391,
5532
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
5533
5533
  },
5534
5534
  {
5535
5535
  "path": "skills/loom-review/.loom-runtime/shared/scripts/loom_init.py",
@@ -6138,13 +6138,13 @@
6138
6138
  },
6139
6139
  {
6140
6140
  "path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py",
6141
- "bytes": 287428,
6142
- "sha256": "48bf635cb9fb4f28b38309a81ecb67a48b489d72f83c4907d972230481848ddb"
6141
+ "bytes": 291476,
6142
+ "sha256": "8a5c1ccde7193facdb348896a3dda9f19b42ea874da1c4744e402e07f951ba50"
6143
6143
  },
6144
6144
  {
6145
6145
  "path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_flow.py",
6146
- "bytes": 296151,
6147
- "sha256": "f74afa96460de82ef279ec78a0468906ca1db26b867a4fc810025164ae8f7146"
6146
+ "bytes": 296391,
6147
+ "sha256": "b35e0e598743b84c21d8ec9b56ff0146a411c0aaf8c29a982d7a21d53044a808"
6148
6148
  },
6149
6149
  {
6150
6150
  "path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_init.py",
@@ -18,7 +18,7 @@ from pathlib import Path
18
18
 
19
19
  from fact_chain_support import inspect_fact_chain
20
20
  from governance_surface import build_governance_surface
21
- from loom_flow import repo_specific_requirements_payload
21
+ from loom_flow import repo_specific_requirements_payload, review_head_binding
22
22
  from runtime_paths import repo_local_root
23
23
 
24
24
  TOP_LEVEL_DIRS = (
@@ -2211,6 +2211,15 @@ def check_demo_fact_chain(root: Path) -> list[Failure]:
2211
2211
  failures.append(Failure("demo-fact-chain", detail))
2212
2212
  if report and report.get("fact_chain", {}).get("entry_points", {}).get("status_surface") != ".loom/status/current.md":
2213
2213
  failures.append(Failure("demo-fact-chain", "demo fact chain must point status_surface to `.loom/status/current.md`"))
2214
+ head_result = run_command(root, ["git", "rev-parse", "HEAD"], timeout_seconds=30)
2215
+ if head_result.returncode == 0:
2216
+ head_binding, head_errors = review_head_binding(
2217
+ root,
2218
+ reviewed_head=head_result.stdout.strip(),
2219
+ allowed_paths=set(),
2220
+ )
2221
+ if head_errors or head_binding.get("status") != "fresh":
2222
+ failures.append(Failure("demo-fact-chain", "review head binding must report `fresh` for the current HEAD"))
2214
2223
  with tempfile.TemporaryDirectory(prefix="loom-check-metadata-spoof-") as tmp:
2215
2224
  spoof_target = Path(tmp) / "new-project"
2216
2225
  shutil.copytree(target, spoof_target)
@@ -4421,6 +4430,65 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
4421
4430
  elif checkpoint_merge_payload.get("result") != "pass":
4422
4431
  failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must pass for the positive chain"))
4423
4432
 
4433
+ payload, error = load_command_json(
4434
+ root,
4435
+ [
4436
+ "python3",
4437
+ str(install_root / "shared" / "scripts" / "loom_flow.py"),
4438
+ "recovery",
4439
+ "writeback",
4440
+ "--target",
4441
+ str(positive_target),
4442
+ "--item",
4443
+ "INIT-0001",
4444
+ "--current-checkpoint",
4445
+ "merge checkpoint",
4446
+ "--current-stop",
4447
+ "Only Loom carrier status changed after review.",
4448
+ "--next-step",
4449
+ "Confirm carrier-only review head binding remains consumable.",
4450
+ "--latest-validation-summary",
4451
+ positive_summary,
4452
+ ],
4453
+ )
4454
+ if error:
4455
+ failures.append(Failure("daily-execution-cli", f"`installed recovery writeback carrier-only` failed: {error}"))
4456
+ elif payload.get("result") != "pass":
4457
+ failures.append(Failure("daily-execution-cli", "`installed recovery writeback carrier-only` must pass"))
4458
+ git_add = run_command(root, ["git", "add", "-f", ".loom/progress/INIT-0001.md", ".loom/status/current.md"], cwd=positive_target)
4459
+ if git_add.returncode != 0:
4460
+ detail = git_add.stderr.strip() or git_add.stdout.strip() or "git add failed"
4461
+ failures.append(Failure("daily-execution-cli", f"`installed carrier-only commit` add failed: {detail}"))
4462
+ else:
4463
+ git_commit = run_command(
4464
+ root,
4465
+ ["git", "commit", "-m", "refresh carriers after review for #354"],
4466
+ cwd=positive_target,
4467
+ )
4468
+ if git_commit.returncode != 0:
4469
+ detail = git_commit.stderr.strip() or git_commit.stdout.strip() or "git commit failed"
4470
+ failures.append(Failure("daily-execution-cli", f"`installed carrier-only commit` failed: {detail}"))
4471
+
4472
+ payload, error = load_command_json(
4473
+ root,
4474
+ [
4475
+ "python3",
4476
+ str(install_root / "shared" / "scripts" / "loom_flow.py"),
4477
+ "checkpoint",
4478
+ "merge",
4479
+ "--target",
4480
+ str(positive_target),
4481
+ "--item",
4482
+ "INIT-0001",
4483
+ ],
4484
+ )
4485
+ if error:
4486
+ failures.append(Failure("daily-execution-cli", f"`installed checkpoint merge` carrier-only failed: {error}"))
4487
+ elif payload.get("result") != "pass":
4488
+ failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must pass for carrier-only review head drift"))
4489
+ elif "carrier-only" not in json.dumps(payload, ensure_ascii=False):
4490
+ failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must expose carrier-only review head binding"))
4491
+
4424
4492
  broken_install = tmp_root / "broken-install" / "skills"
4425
4493
  shutil.copytree(root / "skills", broken_install)
4426
4494
  (broken_install / "install-layout.json").unlink()
@@ -4601,6 +4669,8 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
4601
4669
  failures.append(Failure("daily-execution-cli", f"`installed checkpoint merge` drift negative failed: {error}"))
4602
4670
  elif payload.get("result") != "block":
4603
4671
  failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must block when HEAD drifts beyond Loom carriers"))
4672
+ elif "implementation-drift-only" not in json.dumps(payload, ensure_ascii=False):
4673
+ failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` drift negative must expose implementation-drift-only review head binding"))
4604
4674
 
4605
4675
  gh_auth_probe = None
4606
4676
  if shutil.which("gh") is not None:
@@ -1329,7 +1329,7 @@ def review_head_binding(
1329
1329
  if not isinstance(current_head, str) or not current_head.strip():
1330
1330
  return payload, ["current HEAD is unavailable"]
1331
1331
  if reviewed_head == current_head:
1332
- payload["status"] = "current"
1332
+ payload["status"] = "fresh"
1333
1333
  payload["stale"] = False
1334
1334
  return payload, []
1335
1335
 
@@ -1345,6 +1345,11 @@ def review_head_binding(
1345
1345
  payload["stale"] = False
1346
1346
  return payload, []
1347
1347
 
1348
+ if disallowed_paths and len(disallowed_paths) == len(changed_paths):
1349
+ payload["status"] = "implementation-drift-only"
1350
+ payload["stale"] = True
1351
+ return payload, ["review artifact has implementation drift after review"]
1352
+
1348
1353
  payload["status"] = "stale"
1349
1354
  payload["stale"] = True
1350
1355
  if not changed_paths:
@@ -1372,7 +1377,7 @@ def spec_review_head_binding(
1372
1377
  if not isinstance(current_head, str) or not current_head.strip():
1373
1378
  return payload, ["current HEAD is unavailable"]
1374
1379
  if reviewed_head == current_head:
1375
- payload["status"] = "current"
1380
+ payload["status"] = "fresh"
1376
1381
  payload["stale"] = False
1377
1382
  return payload, []
1378
1383
 
@@ -18,7 +18,7 @@ from pathlib import Path
18
18
 
19
19
  from fact_chain_support import inspect_fact_chain
20
20
  from governance_surface import build_governance_surface
21
- from loom_flow import repo_specific_requirements_payload
21
+ from loom_flow import repo_specific_requirements_payload, review_head_binding
22
22
  from runtime_paths import repo_local_root
23
23
 
24
24
  TOP_LEVEL_DIRS = (
@@ -2211,6 +2211,15 @@ def check_demo_fact_chain(root: Path) -> list[Failure]:
2211
2211
  failures.append(Failure("demo-fact-chain", detail))
2212
2212
  if report and report.get("fact_chain", {}).get("entry_points", {}).get("status_surface") != ".loom/status/current.md":
2213
2213
  failures.append(Failure("demo-fact-chain", "demo fact chain must point status_surface to `.loom/status/current.md`"))
2214
+ head_result = run_command(root, ["git", "rev-parse", "HEAD"], timeout_seconds=30)
2215
+ if head_result.returncode == 0:
2216
+ head_binding, head_errors = review_head_binding(
2217
+ root,
2218
+ reviewed_head=head_result.stdout.strip(),
2219
+ allowed_paths=set(),
2220
+ )
2221
+ if head_errors or head_binding.get("status") != "fresh":
2222
+ failures.append(Failure("demo-fact-chain", "review head binding must report `fresh` for the current HEAD"))
2214
2223
  with tempfile.TemporaryDirectory(prefix="loom-check-metadata-spoof-") as tmp:
2215
2224
  spoof_target = Path(tmp) / "new-project"
2216
2225
  shutil.copytree(target, spoof_target)
@@ -4421,6 +4430,65 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
4421
4430
  elif checkpoint_merge_payload.get("result") != "pass":
4422
4431
  failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must pass for the positive chain"))
4423
4432
 
4433
+ payload, error = load_command_json(
4434
+ root,
4435
+ [
4436
+ "python3",
4437
+ str(install_root / "shared" / "scripts" / "loom_flow.py"),
4438
+ "recovery",
4439
+ "writeback",
4440
+ "--target",
4441
+ str(positive_target),
4442
+ "--item",
4443
+ "INIT-0001",
4444
+ "--current-checkpoint",
4445
+ "merge checkpoint",
4446
+ "--current-stop",
4447
+ "Only Loom carrier status changed after review.",
4448
+ "--next-step",
4449
+ "Confirm carrier-only review head binding remains consumable.",
4450
+ "--latest-validation-summary",
4451
+ positive_summary,
4452
+ ],
4453
+ )
4454
+ if error:
4455
+ failures.append(Failure("daily-execution-cli", f"`installed recovery writeback carrier-only` failed: {error}"))
4456
+ elif payload.get("result") != "pass":
4457
+ failures.append(Failure("daily-execution-cli", "`installed recovery writeback carrier-only` must pass"))
4458
+ git_add = run_command(root, ["git", "add", "-f", ".loom/progress/INIT-0001.md", ".loom/status/current.md"], cwd=positive_target)
4459
+ if git_add.returncode != 0:
4460
+ detail = git_add.stderr.strip() or git_add.stdout.strip() or "git add failed"
4461
+ failures.append(Failure("daily-execution-cli", f"`installed carrier-only commit` add failed: {detail}"))
4462
+ else:
4463
+ git_commit = run_command(
4464
+ root,
4465
+ ["git", "commit", "-m", "refresh carriers after review for #354"],
4466
+ cwd=positive_target,
4467
+ )
4468
+ if git_commit.returncode != 0:
4469
+ detail = git_commit.stderr.strip() or git_commit.stdout.strip() or "git commit failed"
4470
+ failures.append(Failure("daily-execution-cli", f"`installed carrier-only commit` failed: {detail}"))
4471
+
4472
+ payload, error = load_command_json(
4473
+ root,
4474
+ [
4475
+ "python3",
4476
+ str(install_root / "shared" / "scripts" / "loom_flow.py"),
4477
+ "checkpoint",
4478
+ "merge",
4479
+ "--target",
4480
+ str(positive_target),
4481
+ "--item",
4482
+ "INIT-0001",
4483
+ ],
4484
+ )
4485
+ if error:
4486
+ failures.append(Failure("daily-execution-cli", f"`installed checkpoint merge` carrier-only failed: {error}"))
4487
+ elif payload.get("result") != "pass":
4488
+ failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must pass for carrier-only review head drift"))
4489
+ elif "carrier-only" not in json.dumps(payload, ensure_ascii=False):
4490
+ failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must expose carrier-only review head binding"))
4491
+
4424
4492
  broken_install = tmp_root / "broken-install" / "skills"
4425
4493
  shutil.copytree(root / "skills", broken_install)
4426
4494
  (broken_install / "install-layout.json").unlink()
@@ -4601,6 +4669,8 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
4601
4669
  failures.append(Failure("daily-execution-cli", f"`installed checkpoint merge` drift negative failed: {error}"))
4602
4670
  elif payload.get("result") != "block":
4603
4671
  failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must block when HEAD drifts beyond Loom carriers"))
4672
+ elif "implementation-drift-only" not in json.dumps(payload, ensure_ascii=False):
4673
+ failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` drift negative must expose implementation-drift-only review head binding"))
4604
4674
 
4605
4675
  gh_auth_probe = None
4606
4676
  if shutil.which("gh") is not None:
@@ -1329,7 +1329,7 @@ def review_head_binding(
1329
1329
  if not isinstance(current_head, str) or not current_head.strip():
1330
1330
  return payload, ["current HEAD is unavailable"]
1331
1331
  if reviewed_head == current_head:
1332
- payload["status"] = "current"
1332
+ payload["status"] = "fresh"
1333
1333
  payload["stale"] = False
1334
1334
  return payload, []
1335
1335
 
@@ -1345,6 +1345,11 @@ def review_head_binding(
1345
1345
  payload["stale"] = False
1346
1346
  return payload, []
1347
1347
 
1348
+ if disallowed_paths and len(disallowed_paths) == len(changed_paths):
1349
+ payload["status"] = "implementation-drift-only"
1350
+ payload["stale"] = True
1351
+ return payload, ["review artifact has implementation drift after review"]
1352
+
1348
1353
  payload["status"] = "stale"
1349
1354
  payload["stale"] = True
1350
1355
  if not changed_paths:
@@ -1372,7 +1377,7 @@ def spec_review_head_binding(
1372
1377
  if not isinstance(current_head, str) or not current_head.strip():
1373
1378
  return payload, ["current HEAD is unavailable"]
1374
1379
  if reviewed_head == current_head:
1375
- payload["status"] = "current"
1380
+ payload["status"] = "fresh"
1376
1381
  payload["stale"] = False
1377
1382
  return payload, []
1378
1383
 
@@ -18,7 +18,7 @@ from pathlib import Path
18
18
 
19
19
  from fact_chain_support import inspect_fact_chain
20
20
  from governance_surface import build_governance_surface
21
- from loom_flow import repo_specific_requirements_payload
21
+ from loom_flow import repo_specific_requirements_payload, review_head_binding
22
22
  from runtime_paths import repo_local_root
23
23
 
24
24
  TOP_LEVEL_DIRS = (
@@ -2211,6 +2211,15 @@ def check_demo_fact_chain(root: Path) -> list[Failure]:
2211
2211
  failures.append(Failure("demo-fact-chain", detail))
2212
2212
  if report and report.get("fact_chain", {}).get("entry_points", {}).get("status_surface") != ".loom/status/current.md":
2213
2213
  failures.append(Failure("demo-fact-chain", "demo fact chain must point status_surface to `.loom/status/current.md`"))
2214
+ head_result = run_command(root, ["git", "rev-parse", "HEAD"], timeout_seconds=30)
2215
+ if head_result.returncode == 0:
2216
+ head_binding, head_errors = review_head_binding(
2217
+ root,
2218
+ reviewed_head=head_result.stdout.strip(),
2219
+ allowed_paths=set(),
2220
+ )
2221
+ if head_errors or head_binding.get("status") != "fresh":
2222
+ failures.append(Failure("demo-fact-chain", "review head binding must report `fresh` for the current HEAD"))
2214
2223
  with tempfile.TemporaryDirectory(prefix="loom-check-metadata-spoof-") as tmp:
2215
2224
  spoof_target = Path(tmp) / "new-project"
2216
2225
  shutil.copytree(target, spoof_target)
@@ -4421,6 +4430,65 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
4421
4430
  elif checkpoint_merge_payload.get("result") != "pass":
4422
4431
  failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must pass for the positive chain"))
4423
4432
 
4433
+ payload, error = load_command_json(
4434
+ root,
4435
+ [
4436
+ "python3",
4437
+ str(install_root / "shared" / "scripts" / "loom_flow.py"),
4438
+ "recovery",
4439
+ "writeback",
4440
+ "--target",
4441
+ str(positive_target),
4442
+ "--item",
4443
+ "INIT-0001",
4444
+ "--current-checkpoint",
4445
+ "merge checkpoint",
4446
+ "--current-stop",
4447
+ "Only Loom carrier status changed after review.",
4448
+ "--next-step",
4449
+ "Confirm carrier-only review head binding remains consumable.",
4450
+ "--latest-validation-summary",
4451
+ positive_summary,
4452
+ ],
4453
+ )
4454
+ if error:
4455
+ failures.append(Failure("daily-execution-cli", f"`installed recovery writeback carrier-only` failed: {error}"))
4456
+ elif payload.get("result") != "pass":
4457
+ failures.append(Failure("daily-execution-cli", "`installed recovery writeback carrier-only` must pass"))
4458
+ git_add = run_command(root, ["git", "add", "-f", ".loom/progress/INIT-0001.md", ".loom/status/current.md"], cwd=positive_target)
4459
+ if git_add.returncode != 0:
4460
+ detail = git_add.stderr.strip() or git_add.stdout.strip() or "git add failed"
4461
+ failures.append(Failure("daily-execution-cli", f"`installed carrier-only commit` add failed: {detail}"))
4462
+ else:
4463
+ git_commit = run_command(
4464
+ root,
4465
+ ["git", "commit", "-m", "refresh carriers after review for #354"],
4466
+ cwd=positive_target,
4467
+ )
4468
+ if git_commit.returncode != 0:
4469
+ detail = git_commit.stderr.strip() or git_commit.stdout.strip() or "git commit failed"
4470
+ failures.append(Failure("daily-execution-cli", f"`installed carrier-only commit` failed: {detail}"))
4471
+
4472
+ payload, error = load_command_json(
4473
+ root,
4474
+ [
4475
+ "python3",
4476
+ str(install_root / "shared" / "scripts" / "loom_flow.py"),
4477
+ "checkpoint",
4478
+ "merge",
4479
+ "--target",
4480
+ str(positive_target),
4481
+ "--item",
4482
+ "INIT-0001",
4483
+ ],
4484
+ )
4485
+ if error:
4486
+ failures.append(Failure("daily-execution-cli", f"`installed checkpoint merge` carrier-only failed: {error}"))
4487
+ elif payload.get("result") != "pass":
4488
+ failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must pass for carrier-only review head drift"))
4489
+ elif "carrier-only" not in json.dumps(payload, ensure_ascii=False):
4490
+ failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must expose carrier-only review head binding"))
4491
+
4424
4492
  broken_install = tmp_root / "broken-install" / "skills"
4425
4493
  shutil.copytree(root / "skills", broken_install)
4426
4494
  (broken_install / "install-layout.json").unlink()
@@ -4601,6 +4669,8 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
4601
4669
  failures.append(Failure("daily-execution-cli", f"`installed checkpoint merge` drift negative failed: {error}"))
4602
4670
  elif payload.get("result") != "block":
4603
4671
  failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` must block when HEAD drifts beyond Loom carriers"))
4672
+ elif "implementation-drift-only" not in json.dumps(payload, ensure_ascii=False):
4673
+ failures.append(Failure("daily-execution-cli", "`installed checkpoint merge` drift negative must expose implementation-drift-only review head binding"))
4604
4674
 
4605
4675
  gh_auth_probe = None
4606
4676
  if shutil.which("gh") is not None:
@@ -1329,7 +1329,7 @@ def review_head_binding(
1329
1329
  if not isinstance(current_head, str) or not current_head.strip():
1330
1330
  return payload, ["current HEAD is unavailable"]
1331
1331
  if reviewed_head == current_head:
1332
- payload["status"] = "current"
1332
+ payload["status"] = "fresh"
1333
1333
  payload["stale"] = False
1334
1334
  return payload, []
1335
1335
 
@@ -1345,6 +1345,11 @@ def review_head_binding(
1345
1345
  payload["stale"] = False
1346
1346
  return payload, []
1347
1347
 
1348
+ if disallowed_paths and len(disallowed_paths) == len(changed_paths):
1349
+ payload["status"] = "implementation-drift-only"
1350
+ payload["stale"] = True
1351
+ return payload, ["review artifact has implementation drift after review"]
1352
+
1348
1353
  payload["status"] = "stale"
1349
1354
  payload["stale"] = True
1350
1355
  if not changed_paths:
@@ -1372,7 +1377,7 @@ def spec_review_head_binding(
1372
1377
  if not isinstance(current_head, str) or not current_head.strip():
1373
1378
  return payload, ["current HEAD is unavailable"]
1374
1379
  if reviewed_head == current_head:
1375
- payload["status"] = "current"
1380
+ payload["status"] = "fresh"
1376
1381
  payload["stale"] = False
1377
1382
  return payload, []
1378
1383