@mc-and-his-agents/loom-installer 0.1.16 → 0.1.17
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 +59 -0
- package/payload/plugin/loom/skills/shared/scripts/loom_flow.py +149 -2
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py +59 -0
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_flow.py +149 -2
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py +59 -0
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_flow.py +149 -2
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_check.py +59 -0
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_flow.py +149 -2
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py +59 -0
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_flow.py +149 -2
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py +59 -0
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_flow.py +149 -2
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py +59 -0
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_flow.py +149 -2
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py +59 -0
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_flow.py +149 -2
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_check.py +59 -0
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_flow.py +149 -2
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py +59 -0
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_flow.py +149 -2
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": "c3f5933d41b009b1e0537356221c3bcd7fd910be",
|
|
6
6
|
"source_ref": "main",
|
|
7
|
-
"built_at": "2026-04-25T13:
|
|
7
|
+
"built_at": "2026-04-25T13:23:16+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":
|
|
632
|
-
"sha256": "
|
|
631
|
+
"bytes": 269850,
|
|
632
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
633
633
|
},
|
|
634
634
|
{
|
|
635
635
|
"path": "plugin/loom/skills/shared/scripts/loom_flow.py",
|
|
636
|
-
"bytes":
|
|
637
|
-
"sha256": "
|
|
636
|
+
"bytes": 287204,
|
|
637
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
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":
|
|
1217
|
-
"sha256": "
|
|
1216
|
+
"bytes": 269850,
|
|
1217
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
1218
1218
|
},
|
|
1219
1219
|
{
|
|
1220
1220
|
"path": "skills/loom-adopt/.loom-runtime/shared/scripts/loom_flow.py",
|
|
1221
|
-
"bytes":
|
|
1222
|
-
"sha256": "
|
|
1221
|
+
"bytes": 287204,
|
|
1222
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
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":
|
|
1832
|
-
"sha256": "
|
|
1831
|
+
"bytes": 269850,
|
|
1832
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
1833
1833
|
},
|
|
1834
1834
|
{
|
|
1835
1835
|
"path": "skills/loom-handoff/.loom-runtime/shared/scripts/loom_flow.py",
|
|
1836
|
-
"bytes":
|
|
1837
|
-
"sha256": "
|
|
1836
|
+
"bytes": 287204,
|
|
1837
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
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":
|
|
2447
|
-
"sha256": "
|
|
2446
|
+
"bytes": 269850,
|
|
2447
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
2448
2448
|
},
|
|
2449
2449
|
{
|
|
2450
2450
|
"path": "skills/loom-init/.loom-runtime/shared/scripts/loom_flow.py",
|
|
2451
|
-
"bytes":
|
|
2452
|
-
"sha256": "
|
|
2451
|
+
"bytes": 287204,
|
|
2452
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
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":
|
|
3067
|
-
"sha256": "
|
|
3066
|
+
"bytes": 269850,
|
|
3067
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
3068
3068
|
},
|
|
3069
3069
|
{
|
|
3070
3070
|
"path": "skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_flow.py",
|
|
3071
|
-
"bytes":
|
|
3072
|
-
"sha256": "
|
|
3071
|
+
"bytes": 287204,
|
|
3072
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
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":
|
|
3682
|
-
"sha256": "
|
|
3681
|
+
"bytes": 269850,
|
|
3682
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
3683
3683
|
},
|
|
3684
3684
|
{
|
|
3685
3685
|
"path": "skills/loom-pre-review/.loom-runtime/shared/scripts/loom_flow.py",
|
|
3686
|
-
"bytes":
|
|
3687
|
-
"sha256": "
|
|
3686
|
+
"bytes": 287204,
|
|
3687
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
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":
|
|
4297
|
-
"sha256": "
|
|
4296
|
+
"bytes": 269850,
|
|
4297
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
4298
4298
|
},
|
|
4299
4299
|
{
|
|
4300
4300
|
"path": "skills/loom-resume/.loom-runtime/shared/scripts/loom_flow.py",
|
|
4301
|
-
"bytes":
|
|
4302
|
-
"sha256": "
|
|
4301
|
+
"bytes": 287204,
|
|
4302
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
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":
|
|
4912
|
-
"sha256": "
|
|
4911
|
+
"bytes": 269850,
|
|
4912
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
4913
4913
|
},
|
|
4914
4914
|
{
|
|
4915
4915
|
"path": "skills/loom-retire/.loom-runtime/shared/scripts/loom_flow.py",
|
|
4916
|
-
"bytes":
|
|
4917
|
-
"sha256": "
|
|
4916
|
+
"bytes": 287204,
|
|
4917
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
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":
|
|
5527
|
-
"sha256": "
|
|
5526
|
+
"bytes": 269850,
|
|
5527
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
5528
5528
|
},
|
|
5529
5529
|
{
|
|
5530
5530
|
"path": "skills/loom-review/.loom-runtime/shared/scripts/loom_flow.py",
|
|
5531
|
-
"bytes":
|
|
5532
|
-
"sha256": "
|
|
5531
|
+
"bytes": 287204,
|
|
5532
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
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":
|
|
6142
|
-
"sha256": "
|
|
6141
|
+
"bytes": 269850,
|
|
6142
|
+
"sha256": "15eecced57199f79c76774b2a629cae291c610d30b64b29a31ef60f4bad65ec7"
|
|
6143
6143
|
},
|
|
6144
6144
|
{
|
|
6145
6145
|
"path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_flow.py",
|
|
6146
|
-
"bytes":
|
|
6147
|
-
"sha256": "
|
|
6146
|
+
"bytes": 287204,
|
|
6147
|
+
"sha256": "d33bde08ceeafa70ddad41ba1c6f8373e0366da08a9b17484ce0ad3683f83960"
|
|
6148
6148
|
},
|
|
6149
6149
|
{
|
|
6150
6150
|
"path": "skills/loom-spec-review/.loom-runtime/shared/scripts/loom_init.py",
|
|
@@ -93,6 +93,7 @@ CORE_DOCS = (
|
|
|
93
93
|
"docs/evidence/extraction-ledger.md",
|
|
94
94
|
"docs/evidence/landing-map.md",
|
|
95
95
|
"docs/evidence/validations/validation-closeout-reconciliation-blocking-gate.md",
|
|
96
|
+
"docs/evidence/validations/validation-adoption-maturity-upgrade-automation.md",
|
|
96
97
|
"docs/evidence/validations/validation-github-profile-binding-orchestration.md",
|
|
97
98
|
"docs/evidence/validations/validation-github-profile-drift-reconciliation.md",
|
|
98
99
|
"docs/evidence/validations/validation-github-profile-graphql-budget-guard.md",
|
|
@@ -1329,6 +1330,42 @@ def require_github_binding_payload(
|
|
|
1329
1330
|
failures.append(Failure(category, f"{context}.binding findings must fallback to `github-profile-binding`"))
|
|
1330
1331
|
|
|
1331
1332
|
|
|
1333
|
+
def require_governance_upgrade_payload(
|
|
1334
|
+
failures: list[Failure],
|
|
1335
|
+
*,
|
|
1336
|
+
category: str,
|
|
1337
|
+
context: str,
|
|
1338
|
+
payload: object,
|
|
1339
|
+
) -> None:
|
|
1340
|
+
if not isinstance(payload, dict):
|
|
1341
|
+
failures.append(Failure(category, f"{context} must be an object"))
|
|
1342
|
+
return
|
|
1343
|
+
if payload.get("command") != "governance-profile":
|
|
1344
|
+
failures.append(Failure(category, f"{context} must report `command: governance-profile`"))
|
|
1345
|
+
if payload.get("operation") != "upgrade":
|
|
1346
|
+
failures.append(Failure(category, f"{context} must report `operation: upgrade`"))
|
|
1347
|
+
if payload.get("schema_version") != "loom-governance-upgrade/v1":
|
|
1348
|
+
failures.append(Failure(category, f"{context} schema_version must be `loom-governance-upgrade/v1`"))
|
|
1349
|
+
if payload.get("result") not in {"pass", "block"}:
|
|
1350
|
+
failures.append(Failure(category, f"{context} result must be pass/block"))
|
|
1351
|
+
if payload.get("target_maturity") not in {"standard", "strong"}:
|
|
1352
|
+
failures.append(Failure(category, f"{context} target_maturity must be standard/strong"))
|
|
1353
|
+
if not isinstance(payload.get("dry_run"), bool):
|
|
1354
|
+
failures.append(Failure(category, f"{context} dry_run must be boolean"))
|
|
1355
|
+
actions = payload.get("actions")
|
|
1356
|
+
if not isinstance(actions, list) or not actions:
|
|
1357
|
+
failures.append(Failure(category, f"{context} must include non-empty actions"))
|
|
1358
|
+
return
|
|
1359
|
+
for action in actions:
|
|
1360
|
+
if not isinstance(action, dict):
|
|
1361
|
+
failures.append(Failure(category, f"{context} actions must be objects"))
|
|
1362
|
+
continue
|
|
1363
|
+
if action.get("owner") not in {"loom-owned", "repo-owned", "profile"}:
|
|
1364
|
+
failures.append(Failure(category, f"{context} action owner must stay within the stable set"))
|
|
1365
|
+
if action.get("status") not in {"planned", "present"}:
|
|
1366
|
+
failures.append(Failure(category, f"{context} action status must be planned/present"))
|
|
1367
|
+
|
|
1368
|
+
|
|
1332
1369
|
def require_review_record_contract(
|
|
1333
1370
|
failures: list[Failure],
|
|
1334
1371
|
*,
|
|
@@ -2315,6 +2352,21 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
|
|
|
2315
2352
|
["python3", "tools/loom_flow.py", "governance-profile", "upgrade-plan", "--target", "examples/new-project"],
|
|
2316
2353
|
{"pass", "block"},
|
|
2317
2354
|
),
|
|
2355
|
+
(
|
|
2356
|
+
"governance-profile-upgrade",
|
|
2357
|
+
[
|
|
2358
|
+
"python3",
|
|
2359
|
+
"tools/loom_flow.py",
|
|
2360
|
+
"governance-profile",
|
|
2361
|
+
"upgrade",
|
|
2362
|
+
"--target",
|
|
2363
|
+
"examples/new-project",
|
|
2364
|
+
"--to",
|
|
2365
|
+
"standard",
|
|
2366
|
+
"--dry-run",
|
|
2367
|
+
],
|
|
2368
|
+
{"pass"},
|
|
2369
|
+
),
|
|
2318
2370
|
(
|
|
2319
2371
|
"governance-profile-binding",
|
|
2320
2372
|
["python3", "tools/loom_flow.py", "governance-profile", "binding", "--target", "."],
|
|
@@ -2574,6 +2626,13 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
|
|
|
2574
2626
|
context="`governance-profile binding`",
|
|
2575
2627
|
payload=payload,
|
|
2576
2628
|
)
|
|
2629
|
+
if label == "governance-profile-upgrade":
|
|
2630
|
+
require_governance_upgrade_payload(
|
|
2631
|
+
failures,
|
|
2632
|
+
category="daily-execution-cli",
|
|
2633
|
+
context="`governance-profile upgrade`",
|
|
2634
|
+
payload=payload,
|
|
2635
|
+
)
|
|
2577
2636
|
if label == "flow-pre-review":
|
|
2578
2637
|
require_runtime_state_payload(
|
|
2579
2638
|
failures,
|
|
@@ -312,8 +312,12 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
|
312
312
|
"governance-profile",
|
|
313
313
|
help="Read Loom governance maturity and upgrade requirements",
|
|
314
314
|
)
|
|
315
|
-
governance_profile.add_argument("operation", choices=("status", "upgrade-plan", "binding"))
|
|
315
|
+
governance_profile.add_argument("operation", choices=("status", "upgrade-plan", "upgrade", "binding"))
|
|
316
316
|
governance_profile.add_argument("--target", required=True, help="Target repository root")
|
|
317
|
+
governance_profile.add_argument("--to", choices=("standard", "strong"), help="Target maturity for governance-profile upgrade")
|
|
318
|
+
governance_profile.add_argument("--dry-run", action="store_true", default=True, help="Preview upgrade actions without writing files; this is the default")
|
|
319
|
+
governance_profile.add_argument("--apply", dest="dry_run", action="store_false", help="Apply Loom-owned scaffold writes")
|
|
320
|
+
governance_profile.add_argument("--force", action="store_true", help="Allow replacement of existing Loom-owned scaffold files during upgrade apply")
|
|
317
321
|
governance_profile.add_argument("--owner", help="GitHub owner; auto-detected from origin when omitted")
|
|
318
322
|
governance_profile.add_argument("--repo", dest="repo_name", help="GitHub repository name; auto-detected from origin when omitted")
|
|
319
323
|
governance_profile.add_argument("--phase", type=int, help="GitHub Phase issue number")
|
|
@@ -322,7 +326,6 @@ def parse_args(argv: list[str]) -> argparse.Namespace:
|
|
|
322
326
|
governance_profile.add_argument("--pr", type=int, help="GitHub implementation PR number")
|
|
323
327
|
governance_profile.add_argument("--branch", help="GitHub branch name bound to the work item")
|
|
324
328
|
governance_profile.add_argument("--sync", action="store_true", help="Preview host binding repairs; writes are intentionally disabled in this phase")
|
|
325
|
-
governance_profile.add_argument("--dry-run", action="store_true", help="Preview binding sync actions without changing GitHub state")
|
|
326
329
|
|
|
327
330
|
flow = subparsers.add_parser("flow", help="Run a bundled high-frequency Loom flow")
|
|
328
331
|
flow.add_argument("operation", choices=("pre-review", "review", "spec-review", "resume", "handoff", "merge-ready"))
|
|
@@ -3573,6 +3576,141 @@ def governance_profile_payload(target_root: Path, operation: str) -> dict[str, A
|
|
|
3573
3576
|
}
|
|
3574
3577
|
|
|
3575
3578
|
|
|
3579
|
+
UPGRADE_SCAFFOLD: dict[str, dict[str, str]] = {
|
|
3580
|
+
".loom/companion/manifest.json": json.dumps(
|
|
3581
|
+
{
|
|
3582
|
+
"schema_version": "loom-repo-companion-manifest/v1",
|
|
3583
|
+
"companion_entry": ".loom/companion/AGENTS.md",
|
|
3584
|
+
"repo_interface": ".loom/companion/repo-interface.json",
|
|
3585
|
+
},
|
|
3586
|
+
ensure_ascii=False,
|
|
3587
|
+
indent=2,
|
|
3588
|
+
)
|
|
3589
|
+
+ "\n",
|
|
3590
|
+
".loom/companion/repo-interface.json": json.dumps(
|
|
3591
|
+
{
|
|
3592
|
+
"schema_version": "loom-repo-interface/v2",
|
|
3593
|
+
"companion_entry": ".loom/companion/AGENTS.md",
|
|
3594
|
+
"repo_specific_requirements": {"review": [], "merge_ready": [], "closeout": []},
|
|
3595
|
+
"specialized_gates": [],
|
|
3596
|
+
"metadata_contract": {"fields": []},
|
|
3597
|
+
"context_schema": {"fields": []},
|
|
3598
|
+
},
|
|
3599
|
+
ensure_ascii=False,
|
|
3600
|
+
indent=2,
|
|
3601
|
+
)
|
|
3602
|
+
+ "\n",
|
|
3603
|
+
".loom/companion/interop.json": json.dumps(
|
|
3604
|
+
{
|
|
3605
|
+
"schema_version": "loom-repo-interop/v1",
|
|
3606
|
+
"host_adapters": [],
|
|
3607
|
+
"repo_native_carriers": [],
|
|
3608
|
+
"shadow_surfaces": {},
|
|
3609
|
+
},
|
|
3610
|
+
ensure_ascii=False,
|
|
3611
|
+
indent=2,
|
|
3612
|
+
)
|
|
3613
|
+
+ "\n",
|
|
3614
|
+
".loom/companion/AGENTS.md": "# Loom Repo Companion\n\n本文件承接 repo-local governance residue;Loom core 与 GitHub profile 规则仍以上游合同为准。\n",
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
|
|
3618
|
+
def governance_upgrade_actions(target_root: Path, target_level: str, maturity: dict[str, Any]) -> list[dict[str, Any]]:
|
|
3619
|
+
missing_by_level = maturity.get("missing_by_level")
|
|
3620
|
+
missing = missing_by_level.get(target_level, []) if isinstance(missing_by_level, dict) else []
|
|
3621
|
+
actions: list[dict[str, Any]] = []
|
|
3622
|
+
for relative, content in UPGRADE_SCAFFOLD.items():
|
|
3623
|
+
path = target_root / relative
|
|
3624
|
+
owner = "loom-owned" if relative.startswith(".loom/") else "repo-owned"
|
|
3625
|
+
actions.append(
|
|
3626
|
+
{
|
|
3627
|
+
"action": "write_scaffold" if not path.exists() else "keep_existing",
|
|
3628
|
+
"path": relative,
|
|
3629
|
+
"owner": owner,
|
|
3630
|
+
"status": "present" if path.exists() else "planned",
|
|
3631
|
+
"reason": "required by governance profile upgrade path",
|
|
3632
|
+
"bytes": len(content.encode("utf-8")),
|
|
3633
|
+
}
|
|
3634
|
+
)
|
|
3635
|
+
for item in missing if isinstance(missing, list) else []:
|
|
3636
|
+
actions.append(
|
|
3637
|
+
{
|
|
3638
|
+
"action": "satisfy_missing_input",
|
|
3639
|
+
"id": item,
|
|
3640
|
+
"owner": "loom-owned" if str(item) in {"repo_interface", "repo_interop"} else "profile",
|
|
3641
|
+
"status": "planned",
|
|
3642
|
+
"reason": f"`{target_level}` maturity currently reports this missing input.",
|
|
3643
|
+
}
|
|
3644
|
+
)
|
|
3645
|
+
return actions
|
|
3646
|
+
|
|
3647
|
+
|
|
3648
|
+
def governance_profile_upgrade_payload(
|
|
3649
|
+
*,
|
|
3650
|
+
target_root: Path,
|
|
3651
|
+
target_level: str | None,
|
|
3652
|
+
dry_run: bool,
|
|
3653
|
+
force: bool,
|
|
3654
|
+
) -> dict[str, Any]:
|
|
3655
|
+
if target_level is None:
|
|
3656
|
+
return {
|
|
3657
|
+
"command": "governance-profile",
|
|
3658
|
+
"operation": "upgrade",
|
|
3659
|
+
"result": "block",
|
|
3660
|
+
"summary": "governance profile upgrade requires `--to standard` or `--to strong`.",
|
|
3661
|
+
"missing_inputs": ["to"],
|
|
3662
|
+
"fallback_to": "adoption",
|
|
3663
|
+
}
|
|
3664
|
+
base = governance_profile_payload(target_root, "upgrade-plan")
|
|
3665
|
+
maturity = base.get("maturity") if isinstance(base.get("maturity"), dict) else {}
|
|
3666
|
+
actions = governance_upgrade_actions(target_root, target_level, maturity if isinstance(maturity, dict) else {})
|
|
3667
|
+
blockers: list[str] = []
|
|
3668
|
+
written_files: list[str] = []
|
|
3669
|
+
if not dry_run:
|
|
3670
|
+
for action in actions:
|
|
3671
|
+
if action.get("action") != "write_scaffold":
|
|
3672
|
+
continue
|
|
3673
|
+
relative = action.get("path")
|
|
3674
|
+
if not isinstance(relative, str):
|
|
3675
|
+
continue
|
|
3676
|
+
if action.get("owner") != "loom-owned":
|
|
3677
|
+
blockers.append(f"{relative} is repo-owned")
|
|
3678
|
+
continue
|
|
3679
|
+
path = target_root / relative
|
|
3680
|
+
if path.exists() and not force:
|
|
3681
|
+
blockers.append(f"{relative} already exists; use --force to replace Loom-owned scaffold")
|
|
3682
|
+
continue
|
|
3683
|
+
content = UPGRADE_SCAFFOLD.get(relative)
|
|
3684
|
+
if content is None:
|
|
3685
|
+
blockers.append(f"{relative} has no scaffold content")
|
|
3686
|
+
continue
|
|
3687
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
3688
|
+
path.write_text(content, encoding="utf-8")
|
|
3689
|
+
written_files.append(relative)
|
|
3690
|
+
result = "block" if blockers else "pass"
|
|
3691
|
+
return {
|
|
3692
|
+
"command": "governance-profile",
|
|
3693
|
+
"operation": "upgrade",
|
|
3694
|
+
"schema_version": "loom-governance-upgrade/v1",
|
|
3695
|
+
"result": result,
|
|
3696
|
+
"summary": (
|
|
3697
|
+
f"governance profile upgrade toward `{target_level}` produced a dry-run action plan."
|
|
3698
|
+
if dry_run and result == "pass"
|
|
3699
|
+
else f"governance profile upgrade toward `{target_level}` applied Loom-owned scaffold writes."
|
|
3700
|
+
if result == "pass"
|
|
3701
|
+
else f"governance profile upgrade toward `{target_level}` is blocked by unsafe writes."
|
|
3702
|
+
),
|
|
3703
|
+
"missing_inputs": blockers,
|
|
3704
|
+
"fallback_to": None if result == "pass" else "adoption",
|
|
3705
|
+
"target_maturity": target_level,
|
|
3706
|
+
"dry_run": dry_run,
|
|
3707
|
+
"force": force,
|
|
3708
|
+
"actions": actions,
|
|
3709
|
+
"written_files": written_files,
|
|
3710
|
+
"maturity": maturity,
|
|
3711
|
+
}
|
|
3712
|
+
|
|
3713
|
+
|
|
3576
3714
|
def issue_binding_entry(role: str, number: int | None, payload: dict[str, Any] | None, errors: list[str]) -> dict[str, Any]:
|
|
3577
3715
|
status = "present" if payload is not None else "missing"
|
|
3578
3716
|
if errors:
|
|
@@ -3800,6 +3938,15 @@ def github_binding_payload(
|
|
|
3800
3938
|
|
|
3801
3939
|
def handle_governance_profile(args: argparse.Namespace) -> int:
|
|
3802
3940
|
target_root = Path(args.target).expanduser().resolve()
|
|
3941
|
+
if args.operation == "upgrade":
|
|
3942
|
+
return emit(
|
|
3943
|
+
governance_profile_upgrade_payload(
|
|
3944
|
+
target_root=target_root,
|
|
3945
|
+
target_level=args.to,
|
|
3946
|
+
dry_run=args.dry_run,
|
|
3947
|
+
force=args.force,
|
|
3948
|
+
)
|
|
3949
|
+
)
|
|
3803
3950
|
if args.operation == "binding":
|
|
3804
3951
|
return emit(
|
|
3805
3952
|
github_binding_payload(
|
|
@@ -93,6 +93,7 @@ CORE_DOCS = (
|
|
|
93
93
|
"docs/evidence/extraction-ledger.md",
|
|
94
94
|
"docs/evidence/landing-map.md",
|
|
95
95
|
"docs/evidence/validations/validation-closeout-reconciliation-blocking-gate.md",
|
|
96
|
+
"docs/evidence/validations/validation-adoption-maturity-upgrade-automation.md",
|
|
96
97
|
"docs/evidence/validations/validation-github-profile-binding-orchestration.md",
|
|
97
98
|
"docs/evidence/validations/validation-github-profile-drift-reconciliation.md",
|
|
98
99
|
"docs/evidence/validations/validation-github-profile-graphql-budget-guard.md",
|
|
@@ -1329,6 +1330,42 @@ def require_github_binding_payload(
|
|
|
1329
1330
|
failures.append(Failure(category, f"{context}.binding findings must fallback to `github-profile-binding`"))
|
|
1330
1331
|
|
|
1331
1332
|
|
|
1333
|
+
def require_governance_upgrade_payload(
|
|
1334
|
+
failures: list[Failure],
|
|
1335
|
+
*,
|
|
1336
|
+
category: str,
|
|
1337
|
+
context: str,
|
|
1338
|
+
payload: object,
|
|
1339
|
+
) -> None:
|
|
1340
|
+
if not isinstance(payload, dict):
|
|
1341
|
+
failures.append(Failure(category, f"{context} must be an object"))
|
|
1342
|
+
return
|
|
1343
|
+
if payload.get("command") != "governance-profile":
|
|
1344
|
+
failures.append(Failure(category, f"{context} must report `command: governance-profile`"))
|
|
1345
|
+
if payload.get("operation") != "upgrade":
|
|
1346
|
+
failures.append(Failure(category, f"{context} must report `operation: upgrade`"))
|
|
1347
|
+
if payload.get("schema_version") != "loom-governance-upgrade/v1":
|
|
1348
|
+
failures.append(Failure(category, f"{context} schema_version must be `loom-governance-upgrade/v1`"))
|
|
1349
|
+
if payload.get("result") not in {"pass", "block"}:
|
|
1350
|
+
failures.append(Failure(category, f"{context} result must be pass/block"))
|
|
1351
|
+
if payload.get("target_maturity") not in {"standard", "strong"}:
|
|
1352
|
+
failures.append(Failure(category, f"{context} target_maturity must be standard/strong"))
|
|
1353
|
+
if not isinstance(payload.get("dry_run"), bool):
|
|
1354
|
+
failures.append(Failure(category, f"{context} dry_run must be boolean"))
|
|
1355
|
+
actions = payload.get("actions")
|
|
1356
|
+
if not isinstance(actions, list) or not actions:
|
|
1357
|
+
failures.append(Failure(category, f"{context} must include non-empty actions"))
|
|
1358
|
+
return
|
|
1359
|
+
for action in actions:
|
|
1360
|
+
if not isinstance(action, dict):
|
|
1361
|
+
failures.append(Failure(category, f"{context} actions must be objects"))
|
|
1362
|
+
continue
|
|
1363
|
+
if action.get("owner") not in {"loom-owned", "repo-owned", "profile"}:
|
|
1364
|
+
failures.append(Failure(category, f"{context} action owner must stay within the stable set"))
|
|
1365
|
+
if action.get("status") not in {"planned", "present"}:
|
|
1366
|
+
failures.append(Failure(category, f"{context} action status must be planned/present"))
|
|
1367
|
+
|
|
1368
|
+
|
|
1332
1369
|
def require_review_record_contract(
|
|
1333
1370
|
failures: list[Failure],
|
|
1334
1371
|
*,
|
|
@@ -2315,6 +2352,21 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
|
|
|
2315
2352
|
["python3", "tools/loom_flow.py", "governance-profile", "upgrade-plan", "--target", "examples/new-project"],
|
|
2316
2353
|
{"pass", "block"},
|
|
2317
2354
|
),
|
|
2355
|
+
(
|
|
2356
|
+
"governance-profile-upgrade",
|
|
2357
|
+
[
|
|
2358
|
+
"python3",
|
|
2359
|
+
"tools/loom_flow.py",
|
|
2360
|
+
"governance-profile",
|
|
2361
|
+
"upgrade",
|
|
2362
|
+
"--target",
|
|
2363
|
+
"examples/new-project",
|
|
2364
|
+
"--to",
|
|
2365
|
+
"standard",
|
|
2366
|
+
"--dry-run",
|
|
2367
|
+
],
|
|
2368
|
+
{"pass"},
|
|
2369
|
+
),
|
|
2318
2370
|
(
|
|
2319
2371
|
"governance-profile-binding",
|
|
2320
2372
|
["python3", "tools/loom_flow.py", "governance-profile", "binding", "--target", "."],
|
|
@@ -2574,6 +2626,13 @@ def check_daily_execution_cli(root: Path) -> list[Failure]:
|
|
|
2574
2626
|
context="`governance-profile binding`",
|
|
2575
2627
|
payload=payload,
|
|
2576
2628
|
)
|
|
2629
|
+
if label == "governance-profile-upgrade":
|
|
2630
|
+
require_governance_upgrade_payload(
|
|
2631
|
+
failures,
|
|
2632
|
+
category="daily-execution-cli",
|
|
2633
|
+
context="`governance-profile upgrade`",
|
|
2634
|
+
payload=payload,
|
|
2635
|
+
)
|
|
2577
2636
|
if label == "flow-pre-review":
|
|
2578
2637
|
require_runtime_state_payload(
|
|
2579
2638
|
failures,
|