@mc-and-his-agents/loom-installer 0.1.112 → 0.1.113
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 +425 -425
- package/payload/plugin/loom/skills/loom-adopt/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-adopt/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-adopt/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-adopt/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-adopt/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-adopt/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-adopt/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-adopt/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-build/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-build/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-build/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-build/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-build/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-build/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-build/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-build/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-build/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-handoff/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-handoff/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-handoff/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-handoff/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-handoff/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-handoff/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-handoff/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-handoff/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-init/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-init/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-init/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-init/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-init/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-init/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-init/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-init/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-init/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-pre-review/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-pre-review/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-pre-review/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-pre-review/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-pre-review/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-pre-review/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-pre-review/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-resume/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-resume/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-resume/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-resume/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-resume/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-resume/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-resume/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-resume/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-retire/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-retire/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-retire/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-retire/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-retire/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-retire/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-retire/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-retire/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-review/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-review/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-review/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-review/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-review/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-review/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-review/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-review/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-review/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-spec-review/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-spec-review/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-spec-review/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-spec-review/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-spec-review/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-spec-review/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-spec-review/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/loom-story/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-story/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/plugin/loom/skills/loom-story/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/plugin/loom/skills/loom-story/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/plugin/loom/skills/loom-story/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/loom-story/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/loom-story/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/loom-story/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/loom-story/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/plugin/loom/skills/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/plugin/loom/skills/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/plugin/loom/skills/shared/scripts/governance_surface.py +1 -0
- package/payload/plugin/loom/skills/shared/scripts/loom_check.py +106 -0
- package/payload/plugin/loom/skills/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-adopt/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-adopt/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-adopt/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-adopt/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-adopt/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-adopt/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-adopt/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-build/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-build/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-build/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-build/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-build/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-build/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-build/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-build/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-build/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-handoff/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-handoff/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-handoff/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-handoff/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-handoff/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-handoff/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-handoff/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-init/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-init/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-init/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-init/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-init/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-init/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-init/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-init/contract.json +2 -0
- package/payload/skills/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-merge-ready/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-merge-ready/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-merge-ready/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-merge-ready/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-pre-review/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-pre-review/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-pre-review/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-pre-review/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-pre-review/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-pre-review/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-pre-review/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-resume/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-resume/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-resume/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-resume/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-resume/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-resume/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-resume/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-retire/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-retire/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-retire/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-retire/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-retire/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-retire/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-retire/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-review/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-review/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-review/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-review/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-review/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-review/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-review/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-spec-review/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-spec-review/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-spec-review/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-spec-review/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-spec-review/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-spec-review/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-spec-review/.loom-runtime/shared/scripts/loom_init.py +232 -6
- package/payload/skills/loom-story/.loom-runtime/loom-adopt/SKILL.md +1 -1
- package/payload/skills/loom-story/.loom-runtime/loom-init/SKILL.md +1 -1
- package/payload/skills/loom-story/.loom-runtime/loom-init/contract.json +2 -0
- package/payload/skills/loom-story/.loom-runtime/loom-init/references/output-contract.md +6 -0
- package/payload/skills/loom-story/.loom-runtime/shared/references/adoption/deep-existing-repo-default.md +4 -0
- package/payload/skills/loom-story/.loom-runtime/shared/references/adoption/repo-companion-contract.md +3 -1
- package/payload/skills/loom-story/.loom-runtime/shared/scripts/governance_surface.py +1 -0
- package/payload/skills/loom-story/.loom-runtime/shared/scripts/loom_check.py +106 -0
- package/payload/skills/loom-story/.loom-runtime/shared/scripts/loom_init.py +232 -6
package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_check.py
CHANGED
|
@@ -4336,6 +4336,28 @@ def check_deep_existing_repo_bootstrap(root: Path) -> list[Failure]:
|
|
|
4336
4336
|
if pr_template:
|
|
4337
4337
|
(target / ".github" / "PULL_REQUEST_TEMPLATE.md").write_text("## Summary\n", encoding="utf-8")
|
|
4338
4338
|
|
|
4339
|
+
attach_only_forbidden_patterns = (
|
|
4340
|
+
".loom/work-items/**",
|
|
4341
|
+
".loom/progress/**",
|
|
4342
|
+
".loom/status/current.md",
|
|
4343
|
+
".loom/reviews/**",
|
|
4344
|
+
".loom/specs/**",
|
|
4345
|
+
)
|
|
4346
|
+
|
|
4347
|
+
def matches_forbidden(path: str, pattern: str) -> bool:
|
|
4348
|
+
if pattern.endswith("/**"):
|
|
4349
|
+
prefix = pattern[:-3]
|
|
4350
|
+
return path == prefix or path.startswith(prefix + "/")
|
|
4351
|
+
return path == pattern
|
|
4352
|
+
|
|
4353
|
+
def forbidden_match(path: object) -> str | None:
|
|
4354
|
+
if not isinstance(path, str):
|
|
4355
|
+
return None
|
|
4356
|
+
for pattern in attach_only_forbidden_patterns:
|
|
4357
|
+
if matches_forbidden(path, pattern):
|
|
4358
|
+
return pattern
|
|
4359
|
+
return None
|
|
4360
|
+
|
|
4339
4361
|
deep_target = tmp_root / "deep-existing"
|
|
4340
4362
|
write_repo(deep_target, validation_entry=True, pr_template=True, workflow_doc=True)
|
|
4341
4363
|
deep_dry_payload, deep_dry_error = load_command_json(
|
|
@@ -4356,6 +4378,8 @@ def check_deep_existing_repo_bootstrap(root: Path) -> list[Failure]:
|
|
|
4356
4378
|
intent = deep_dry_payload.get("adoption_intent")
|
|
4357
4379
|
risk = deep_dry_payload.get("risk_summary")
|
|
4358
4380
|
planned = deep_dry_payload.get("planned_writes")
|
|
4381
|
+
required = deep_dry_payload.get("required_carriers")
|
|
4382
|
+
forbidden = deep_dry_payload.get("forbidden_authored_carriers")
|
|
4359
4383
|
detected = deep_dry_payload.get("detected_repository_mode")
|
|
4360
4384
|
write = deep_dry_payload.get("write")
|
|
4361
4385
|
if not isinstance(intent, dict) or intent.get("effective") != "attach-only":
|
|
@@ -4364,6 +4388,18 @@ def check_deep_existing_repo_bootstrap(root: Path) -> list[Failure]:
|
|
|
4364
4388
|
failures.append(Failure("deep-existing-bootstrap", "`deep-existing dry-run` must report preserved repo-owned truth risk"))
|
|
4365
4389
|
if not isinstance(planned, list) or not planned:
|
|
4366
4390
|
failures.append(Failure("deep-existing-bootstrap", "`deep-existing dry-run` must report planned write targets"))
|
|
4391
|
+
else:
|
|
4392
|
+
for item in planned:
|
|
4393
|
+
if isinstance(item, dict):
|
|
4394
|
+
pattern = forbidden_match(item.get("path"))
|
|
4395
|
+
if pattern:
|
|
4396
|
+
failures.append(Failure("deep-existing-bootstrap", f"`deep-existing dry-run` planned write must not match forbidden carrier `{pattern}`"))
|
|
4397
|
+
if not isinstance(required, list) or not required:
|
|
4398
|
+
failures.append(Failure("deep-existing-bootstrap", "`deep-existing dry-run` must report required carriers"))
|
|
4399
|
+
if not isinstance(forbidden, list) or {
|
|
4400
|
+
item.get("path") for item in forbidden if isinstance(item, dict)
|
|
4401
|
+
} != set(attach_only_forbidden_patterns):
|
|
4402
|
+
failures.append(Failure("deep-existing-bootstrap", "`deep-existing dry-run` must report the full attach-only forbidden carrier list"))
|
|
4367
4403
|
if not isinstance(detected, dict) or detected.get("scenario_key") != "complex-existing":
|
|
4368
4404
|
failures.append(Failure("deep-existing-bootstrap", "`deep-existing dry-run` must report detected repository mode"))
|
|
4369
4405
|
if not isinstance(write, dict) or write.get("enabled") is not False:
|
|
@@ -4391,6 +4427,7 @@ def check_deep_existing_repo_bootstrap(root: Path) -> list[Failure]:
|
|
|
4391
4427
|
recommended = deep_payload.get("recommended_adoption")
|
|
4392
4428
|
verification = deep_payload.get("verification")
|
|
4393
4429
|
governance_surface = deep_payload.get("governance_surface")
|
|
4430
|
+
repo_interface_path = deep_target / ".loom/companion/repo-interface.json"
|
|
4394
4431
|
if not isinstance(recommended, dict) or recommended.get("path") != "deep-existing-repo":
|
|
4395
4432
|
failures.append(Failure("deep-existing-bootstrap", "`deep-existing bootstrap` must select `recommended_adoption.path = deep-existing-repo`"))
|
|
4396
4433
|
run = deep_payload.get("run")
|
|
@@ -4413,12 +4450,79 @@ def check_deep_existing_repo_bootstrap(root: Path) -> list[Failure]:
|
|
|
4413
4450
|
".loom/work-items/INIT-0001.md",
|
|
4414
4451
|
".loom/progress/INIT-0001.md",
|
|
4415
4452
|
".loom/status/current.md",
|
|
4453
|
+
".loom/reviews/INIT-0001.json",
|
|
4454
|
+
".loom/specs/INIT-0001/spec.md",
|
|
4416
4455
|
):
|
|
4417
4456
|
if (deep_target / forbidden).exists():
|
|
4418
4457
|
failures.append(Failure("deep-existing-bootstrap", f"`deep-existing bootstrap` must not generate `{forbidden}`"))
|
|
4419
4458
|
fact_chain = deep_payload.get("fact_chain")
|
|
4420
4459
|
if not isinstance(fact_chain, dict) or fact_chain.get("mode") != "repo-native attach-only":
|
|
4421
4460
|
failures.append(Failure("deep-existing-bootstrap", "`deep-existing bootstrap` must keep `fact_chain.mode = repo-native attach-only`"))
|
|
4461
|
+
if repo_interface_path.exists():
|
|
4462
|
+
repo_interface = json.loads(repo_interface_path.read_text(encoding="utf-8"))
|
|
4463
|
+
host_truth = repo_interface.get("host_truth_locators")
|
|
4464
|
+
if not isinstance(host_truth, dict) or set(host_truth.keys()) != {"work_item", "project_status", "review", "closeout"}:
|
|
4465
|
+
failures.append(Failure("deep-existing-bootstrap", "`deep-existing bootstrap` repo-interface must declare attach-only host truth locators"))
|
|
4466
|
+
|
|
4467
|
+
poisoned_files_target = tmp_root / "deep-existing-poison-files"
|
|
4468
|
+
if deep_target.exists() and (deep_target / ".loom/bin/loom_init.py").exists():
|
|
4469
|
+
shutil.copytree(deep_target, poisoned_files_target)
|
|
4470
|
+
(poisoned_files_target / ".loom/reviews").mkdir(parents=True, exist_ok=True)
|
|
4471
|
+
(poisoned_files_target / ".loom/specs/EXISTING").mkdir(parents=True, exist_ok=True)
|
|
4472
|
+
(poisoned_files_target / ".loom/reviews/EXISTING.json").write_text("{}", encoding="utf-8")
|
|
4473
|
+
(poisoned_files_target / ".loom/specs/EXISTING/spec.md").write_text("# Existing Spec\n", encoding="utf-8")
|
|
4474
|
+
poisoned_payload, poisoned_error = load_command_json(
|
|
4475
|
+
root,
|
|
4476
|
+
[
|
|
4477
|
+
"python3",
|
|
4478
|
+
str(poisoned_files_target / ".loom/bin/loom_init.py"),
|
|
4479
|
+
"verify",
|
|
4480
|
+
"--target",
|
|
4481
|
+
str(poisoned_files_target),
|
|
4482
|
+
],
|
|
4483
|
+
)
|
|
4484
|
+
if poisoned_error:
|
|
4485
|
+
failures.append(Failure("deep-existing-bootstrap", f"`attach-only forbidden file verify` failed: {poisoned_error}"))
|
|
4486
|
+
else:
|
|
4487
|
+
errors_text = json.dumps(poisoned_payload.get("errors", []), ensure_ascii=False) if poisoned_payload else ""
|
|
4488
|
+
if poisoned_payload.get("ok") is not False:
|
|
4489
|
+
failures.append(Failure("deep-existing-bootstrap", "`attach-only forbidden file verify` must fail closed"))
|
|
4490
|
+
if "forbidden authored carrier" not in errors_text or "second truth chain" not in errors_text:
|
|
4491
|
+
failures.append(Failure("deep-existing-bootstrap", "`attach-only forbidden file verify` must explain the second truth-chain risk"))
|
|
4492
|
+
|
|
4493
|
+
poisoned_decl_target = tmp_root / "deep-existing-poison-declarations"
|
|
4494
|
+
if deep_target.exists() and (deep_target / ".loom/bin/loom_init.py").exists():
|
|
4495
|
+
shutil.copytree(deep_target, poisoned_decl_target)
|
|
4496
|
+
init_result_path = poisoned_decl_target / ".loom/bootstrap/init-result.json"
|
|
4497
|
+
manifest_path = poisoned_decl_target / ".loom/bootstrap/manifest.json"
|
|
4498
|
+
init_result = json.loads(init_result_path.read_text(encoding="utf-8"))
|
|
4499
|
+
init_result.setdefault("planned_writes", []).append(
|
|
4500
|
+
{"path": ".loom/work-items/POISON.md", "kind": "work-item", "owner": "loom"}
|
|
4501
|
+
)
|
|
4502
|
+
init_result_path.write_text(json.dumps(init_result, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
4503
|
+
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
|
4504
|
+
manifest.setdefault("artifacts", []).append(
|
|
4505
|
+
{"path": ".loom/progress/POISON.md", "kind": "progress", "source": "generated"}
|
|
4506
|
+
)
|
|
4507
|
+
manifest_path.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
|
|
4508
|
+
poisoned_payload, poisoned_error = load_command_json(
|
|
4509
|
+
root,
|
|
4510
|
+
[
|
|
4511
|
+
"python3",
|
|
4512
|
+
str(poisoned_decl_target / ".loom/bin/loom_init.py"),
|
|
4513
|
+
"verify",
|
|
4514
|
+
"--target",
|
|
4515
|
+
str(poisoned_decl_target),
|
|
4516
|
+
],
|
|
4517
|
+
)
|
|
4518
|
+
if poisoned_error:
|
|
4519
|
+
failures.append(Failure("deep-existing-bootstrap", f"`attach-only forbidden declaration verify` failed: {poisoned_error}"))
|
|
4520
|
+
else:
|
|
4521
|
+
errors_text = json.dumps(poisoned_payload.get("errors", []), ensure_ascii=False) if poisoned_payload else ""
|
|
4522
|
+
if poisoned_payload.get("ok") is not False:
|
|
4523
|
+
failures.append(Failure("deep-existing-bootstrap", "`attach-only forbidden declaration verify` must fail closed"))
|
|
4524
|
+
if "planned_writes" not in errors_text or "manifest.artifacts" not in errors_text:
|
|
4525
|
+
failures.append(Failure("deep-existing-bootstrap", "`attach-only forbidden declaration verify` must name poisoned declaration sources"))
|
|
4422
4526
|
|
|
4423
4527
|
full_target = tmp_root / "full-bootstrap"
|
|
4424
4528
|
write_repo(full_target, validation_entry=False, pr_template=False, workflow_doc=False)
|
|
@@ -4489,6 +4593,8 @@ def check_deep_existing_repo_bootstrap(root: Path) -> list[Failure]:
|
|
|
4489
4593
|
".loom/work-items/INIT-0001.md",
|
|
4490
4594
|
".loom/progress/INIT-0001.md",
|
|
4491
4595
|
".loom/status/current.md",
|
|
4596
|
+
".loom/reviews/INIT-0001.json",
|
|
4597
|
+
".loom/specs/INIT-0001/spec.md",
|
|
4492
4598
|
):
|
|
4493
4599
|
if not (explicit_full_target / required).exists():
|
|
4494
4600
|
failures.append(Failure("deep-existing-bootstrap", f"`explicit execution-control bootstrap sample` must generate `{required}`"))
|
package/payload/plugin/loom/skills/loom-merge-ready/.loom-runtime/shared/scripts/loom_init.py
CHANGED
|
@@ -46,6 +46,55 @@ SCAFFOLD_PROFILES = (
|
|
|
46
46
|
"execution-control",
|
|
47
47
|
"strong-governance",
|
|
48
48
|
)
|
|
49
|
+
ATTACH_ONLY_FORBIDDEN_AUTHORED_CARRIERS = (
|
|
50
|
+
{
|
|
51
|
+
"path": ".loom/work-items/**",
|
|
52
|
+
"reason": "attach-only preserves host-owned work item truth",
|
|
53
|
+
"remediation": "migrate the item to a host truth locator, delete the competing Loom carrier, or rerun with --intent execution-control",
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"path": ".loom/progress/**",
|
|
57
|
+
"reason": "attach-only preserves host-owned recovery/progress truth",
|
|
58
|
+
"remediation": "migrate recovery state to a host truth locator, delete the competing Loom carrier, or rerun with --intent execution-control",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"path": ".loom/status/current.md",
|
|
62
|
+
"reason": "attach-only preserves host-owned project status truth",
|
|
63
|
+
"remediation": "migrate status to a host truth locator, delete the competing Loom carrier, or rerun with --intent execution-control",
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"path": ".loom/reviews/**",
|
|
67
|
+
"reason": "attach-only preserves host-owned PR review or guardian truth",
|
|
68
|
+
"remediation": "migrate review truth to a host truth locator, delete the competing Loom carrier, or rerun with --intent execution-control",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"path": ".loom/specs/**",
|
|
72
|
+
"reason": "attach-only does not author Loom spec truth unless the repo explicitly upgrades intent",
|
|
73
|
+
"remediation": "migrate spec truth to a host locator, delete the competing Loom carrier, or rerun with --intent execution-control",
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
ATTACH_ONLY_HOST_TRUTH_LOCATORS = {
|
|
77
|
+
"work_item": {
|
|
78
|
+
"host_surface": "github_issue",
|
|
79
|
+
"locator": "repo-owned issue tracker",
|
|
80
|
+
"mode": "host_truth_locator",
|
|
81
|
+
},
|
|
82
|
+
"project_status": {
|
|
83
|
+
"host_surface": "github_project",
|
|
84
|
+
"locator": "repo-owned project board or status system",
|
|
85
|
+
"mode": "host_truth_locator",
|
|
86
|
+
},
|
|
87
|
+
"review": {
|
|
88
|
+
"host_surface": "pull_request_review_or_guardian",
|
|
89
|
+
"locator": "repo-owned PR review, guardian, or review gate",
|
|
90
|
+
"mode": "host_truth_locator",
|
|
91
|
+
},
|
|
92
|
+
"closeout": {
|
|
93
|
+
"host_surface": "pull_request_metadata_and_issue_state",
|
|
94
|
+
"locator": "repo-owned PR metadata plus issue state",
|
|
95
|
+
"mode": "host_truth_locator",
|
|
96
|
+
},
|
|
97
|
+
}
|
|
49
98
|
|
|
50
99
|
RUNTIME_ARTIFACT_SOURCES = {
|
|
51
100
|
".loom/bin/loom_init.py": RUNTIME_SOURCE,
|
|
@@ -751,6 +800,160 @@ def profile_writes_artifacts(profile: str) -> bool:
|
|
|
751
800
|
return profile not in {"observe-only", "skill-install-only"}
|
|
752
801
|
|
|
753
802
|
|
|
803
|
+
def forbidden_authored_carriers(profile: str) -> list[dict[str, str]]:
|
|
804
|
+
if profile != "attach-only":
|
|
805
|
+
return []
|
|
806
|
+
return [dict(carrier) for carrier in ATTACH_ONLY_FORBIDDEN_AUTHORED_CARRIERS]
|
|
807
|
+
|
|
808
|
+
|
|
809
|
+
def required_carriers_for_profile(artifacts: list[dict[str, str]], profile: str) -> list[dict[str, str]]:
|
|
810
|
+
if profile in {"observe-only", "skill-install-only"}:
|
|
811
|
+
return []
|
|
812
|
+
return [
|
|
813
|
+
{
|
|
814
|
+
"path": artifact["path"],
|
|
815
|
+
"kind": artifact["kind"],
|
|
816
|
+
"owner": write_owner_for_path(artifact["path"]),
|
|
817
|
+
}
|
|
818
|
+
for artifact in artifacts
|
|
819
|
+
if isinstance(artifact.get("path"), str) and isinstance(artifact.get("kind"), str)
|
|
820
|
+
]
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
def normalize_relative_path(raw_path: object) -> str | None:
|
|
824
|
+
if not isinstance(raw_path, str) or not raw_path:
|
|
825
|
+
return None
|
|
826
|
+
normalized = raw_path.replace("\\", "/")
|
|
827
|
+
while normalized.startswith("./"):
|
|
828
|
+
normalized = normalized[2:]
|
|
829
|
+
if normalized.startswith("/") or normalized.startswith("../") or "/../" in normalized:
|
|
830
|
+
return None
|
|
831
|
+
return normalized
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
def matches_forbidden_authored_carrier(relative_path: str, pattern: str) -> bool:
|
|
835
|
+
if pattern.endswith("/**"):
|
|
836
|
+
prefix = pattern[:-3]
|
|
837
|
+
return relative_path == prefix or relative_path.startswith(prefix + "/")
|
|
838
|
+
return relative_path == pattern
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def forbidden_authored_carrier_for_path(relative_path: str) -> dict[str, str] | None:
|
|
842
|
+
for carrier in ATTACH_ONLY_FORBIDDEN_AUTHORED_CARRIERS:
|
|
843
|
+
pattern = carrier["path"]
|
|
844
|
+
if matches_forbidden_authored_carrier(relative_path, pattern):
|
|
845
|
+
return carrier
|
|
846
|
+
return None
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
def collect_forbidden_authored_carrier_declarations(result: dict[str, object]) -> list[dict[str, str]]:
|
|
850
|
+
declarations: list[dict[str, str]] = []
|
|
851
|
+
|
|
852
|
+
def add(raw_path: object, source: str) -> None:
|
|
853
|
+
relative = normalize_relative_path(raw_path)
|
|
854
|
+
if relative is None:
|
|
855
|
+
return
|
|
856
|
+
carrier = forbidden_authored_carrier_for_path(relative)
|
|
857
|
+
if carrier is not None:
|
|
858
|
+
declarations.append({"path": relative, "pattern": carrier["path"], "source": source})
|
|
859
|
+
|
|
860
|
+
for key in ("initial_artifacts", "planned_writes"):
|
|
861
|
+
entries = result.get(key)
|
|
862
|
+
if isinstance(entries, list):
|
|
863
|
+
for entry in entries:
|
|
864
|
+
if isinstance(entry, dict):
|
|
865
|
+
add(entry.get("path"), key)
|
|
866
|
+
work_items = result.get("initial_work_items")
|
|
867
|
+
if isinstance(work_items, list):
|
|
868
|
+
for work_item in work_items:
|
|
869
|
+
if not isinstance(work_item, dict):
|
|
870
|
+
continue
|
|
871
|
+
artifacts = work_item.get("artifacts")
|
|
872
|
+
if isinstance(artifacts, list):
|
|
873
|
+
for artifact in artifacts:
|
|
874
|
+
add(artifact, "initial_work_items.artifacts")
|
|
875
|
+
write = result.get("write")
|
|
876
|
+
if isinstance(write, dict):
|
|
877
|
+
touched = write.get("touched")
|
|
878
|
+
if isinstance(touched, list):
|
|
879
|
+
for path in touched:
|
|
880
|
+
add(path, "write.touched")
|
|
881
|
+
return declarations
|
|
882
|
+
|
|
883
|
+
|
|
884
|
+
def collect_forbidden_authored_carrier_files(target_root: Path) -> list[dict[str, str]]:
|
|
885
|
+
found: list[dict[str, str]] = []
|
|
886
|
+
for carrier in ATTACH_ONLY_FORBIDDEN_AUTHORED_CARRIERS:
|
|
887
|
+
pattern = carrier["path"]
|
|
888
|
+
if pattern.endswith("/**"):
|
|
889
|
+
prefix = pattern[:-3]
|
|
890
|
+
base = target_root / prefix
|
|
891
|
+
if not base.exists():
|
|
892
|
+
continue
|
|
893
|
+
if base.is_file():
|
|
894
|
+
found.append({"path": prefix, "pattern": pattern, "source": "filesystem"})
|
|
895
|
+
continue
|
|
896
|
+
for path in sorted(candidate for candidate in base.rglob("*") if candidate.is_file()):
|
|
897
|
+
found.append(
|
|
898
|
+
{
|
|
899
|
+
"path": path.relative_to(target_root).as_posix(),
|
|
900
|
+
"pattern": pattern,
|
|
901
|
+
"source": "filesystem",
|
|
902
|
+
}
|
|
903
|
+
)
|
|
904
|
+
continue
|
|
905
|
+
exact = target_root / pattern
|
|
906
|
+
if exact.exists():
|
|
907
|
+
found.append({"path": pattern, "pattern": pattern, "source": "filesystem"})
|
|
908
|
+
return found
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
def collect_forbidden_manifest_declarations(target_root: Path) -> list[dict[str, str]]:
|
|
912
|
+
manifest_path = target_root / ".loom/bootstrap/manifest.json"
|
|
913
|
+
if not manifest_path.exists():
|
|
914
|
+
return []
|
|
915
|
+
try:
|
|
916
|
+
manifest = read_json(manifest_path)
|
|
917
|
+
except json.JSONDecodeError:
|
|
918
|
+
return []
|
|
919
|
+
artifacts = manifest.get("artifacts")
|
|
920
|
+
declarations: list[dict[str, str]] = []
|
|
921
|
+
if not isinstance(artifacts, list):
|
|
922
|
+
return declarations
|
|
923
|
+
for artifact in artifacts:
|
|
924
|
+
if not isinstance(artifact, dict):
|
|
925
|
+
continue
|
|
926
|
+
relative = normalize_relative_path(artifact.get("path"))
|
|
927
|
+
if relative is None:
|
|
928
|
+
continue
|
|
929
|
+
carrier = forbidden_authored_carrier_for_path(relative)
|
|
930
|
+
if carrier is not None:
|
|
931
|
+
declarations.append({"path": relative, "pattern": carrier["path"], "source": "manifest.artifacts"})
|
|
932
|
+
return declarations
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
def attach_only_forbidden_carrier_errors(target_root: Path, result: dict[str, object]) -> list[str]:
|
|
936
|
+
findings = (
|
|
937
|
+
collect_forbidden_authored_carrier_declarations(result)
|
|
938
|
+
+ collect_forbidden_manifest_declarations(target_root)
|
|
939
|
+
+ collect_forbidden_authored_carrier_files(target_root)
|
|
940
|
+
)
|
|
941
|
+
errors: list[str] = []
|
|
942
|
+
seen: set[tuple[str, str, str]] = set()
|
|
943
|
+
for finding in findings:
|
|
944
|
+
key = (finding["source"], finding["path"], finding["pattern"])
|
|
945
|
+
if key in seen:
|
|
946
|
+
continue
|
|
947
|
+
seen.add(key)
|
|
948
|
+
errors.append(
|
|
949
|
+
"attach-only forbidden authored carrier detected in "
|
|
950
|
+
f"{finding['source']}: `{finding['path']}` matches `{finding['pattern']}`; "
|
|
951
|
+
"this would create a second truth chain. Migrate it to the host truth locator, delete it, "
|
|
952
|
+
"or explicitly rerun bootstrap with `--intent execution-control`."
|
|
953
|
+
)
|
|
954
|
+
return errors
|
|
955
|
+
|
|
956
|
+
|
|
754
957
|
def adoption_intent_payload(adoption_path: str, intake: dict[str, object]) -> dict[str, object]:
|
|
755
958
|
requested = str(intake.get("adoption_intent", UNSPECIFIED_ADOPTION_INTENT))
|
|
756
959
|
source = str(intake.get("adoption_intent_source", "unspecified"))
|
|
@@ -1170,6 +1373,7 @@ def intentionally_absent_targets(adoption_path: str, profile: str) -> list[dict[
|
|
|
1170
1373
|
{"path": ".loom/work-items/**", "reason": "attach-only preserves host-owned work item truth"},
|
|
1171
1374
|
{"path": ".loom/progress/**", "reason": "attach-only preserves host-owned recovery truth"},
|
|
1172
1375
|
{"path": ".loom/status/current.md", "reason": "attach-only does not author Loom status truth"},
|
|
1376
|
+
{"path": ".loom/reviews/**", "reason": "attach-only preserves host-owned review truth"},
|
|
1173
1377
|
{"path": ".loom/specs/**", "reason": "attach-only does not author Loom execution specs"},
|
|
1174
1378
|
]
|
|
1175
1379
|
if adoption_path == "defer":
|
|
@@ -1235,6 +1439,7 @@ def build_result(target_root: Path, scenario: str, intake: dict[str, object], in
|
|
|
1235
1439
|
bootstrap_mode=True,
|
|
1236
1440
|
scenario_override=scenario,
|
|
1237
1441
|
)
|
|
1442
|
+
initial_artifact_list = initial_artifacts(target_root, install_pr_template, adoption_path, profile)
|
|
1238
1443
|
result = {
|
|
1239
1444
|
"schema_version": "loom-init-output/v1",
|
|
1240
1445
|
"generator": {
|
|
@@ -1322,7 +1527,7 @@ def build_result(target_root: Path, scenario: str, intake: dict[str, object], in
|
|
|
1322
1527
|
},
|
|
1323
1528
|
}
|
|
1324
1529
|
),
|
|
1325
|
-
"initial_artifacts":
|
|
1530
|
+
"initial_artifacts": initial_artifact_list,
|
|
1326
1531
|
"initial_work_items": initial_work_items(scenario, target_root, adoption_path, install_pr_template, profile),
|
|
1327
1532
|
"validation_and_closing": {
|
|
1328
1533
|
"validation_entry": "python3 .loom/bin/loom_init.py verify --target .",
|
|
@@ -1346,7 +1551,7 @@ def build_result(target_root: Path, scenario: str, intake: dict[str, object], in
|
|
|
1346
1551
|
]
|
|
1347
1552
|
),
|
|
1348
1553
|
"clean_state": (
|
|
1349
|
-
"all generated attach-only Loom artifacts are readable, verified, and do not introduce Loom-
|
|
1554
|
+
"all generated attach-only Loom artifacts are readable, verified, and do not introduce Loom-authored work/progress/status/review/spec truth carriers"
|
|
1350
1555
|
if attach_only
|
|
1351
1556
|
else "all generated light-governance artifacts are readable, verified, and do not introduce Loom-owned work/progress/status carriers"
|
|
1352
1557
|
if profile == "light-governance"
|
|
@@ -1356,7 +1561,7 @@ def build_result(target_root: Path, scenario: str, intake: dict[str, object], in
|
|
|
1356
1561
|
[
|
|
1357
1562
|
"the target repo has a readable root rule entry and attached repo companion entry",
|
|
1358
1563
|
"the attach-only bootstrap metadata and repo-local validation path are verifiable",
|
|
1359
|
-
"the bootstrap manifest
|
|
1564
|
+
"the bootstrap manifest, init-result, planned writes, and filesystem do not expose forbidden Loom-authored truth carriers",
|
|
1360
1565
|
]
|
|
1361
1566
|
if attach_only
|
|
1362
1567
|
else [
|
|
@@ -1386,7 +1591,13 @@ def build_result(target_root: Path, scenario: str, intake: dict[str, object], in
|
|
|
1386
1591
|
}
|
|
1387
1592
|
result["adoption_intent"] = adoption_intent_payload(adoption_path, intake)
|
|
1388
1593
|
result["planned_writes"] = planned
|
|
1594
|
+
result["required_carriers"] = required_carriers_for_profile(initial_artifact_list, profile)
|
|
1389
1595
|
result["intentionally_absent"] = intentionally_absent_targets(adoption_path, profile)
|
|
1596
|
+
result["forbidden_authored_carriers"] = forbidden_authored_carriers(profile)
|
|
1597
|
+
scaffold_profile = result.get("scaffold_profile")
|
|
1598
|
+
if isinstance(scaffold_profile, dict):
|
|
1599
|
+
scaffold_profile["required_carriers"] = result["required_carriers"]
|
|
1600
|
+
scaffold_profile["forbidden_authored_carriers"] = result["forbidden_authored_carriers"]
|
|
1390
1601
|
result["upgrade_triggers"] = upgrade_triggers(deferred if isinstance(deferred, list) else [], profile)
|
|
1391
1602
|
result["risk_summary"] = risk_summary(adoption_path, intake, planned)
|
|
1392
1603
|
return result
|
|
@@ -1626,8 +1837,8 @@ def companion_manifest_payload() -> dict[str, object]:
|
|
|
1626
1837
|
}
|
|
1627
1838
|
|
|
1628
1839
|
|
|
1629
|
-
def repo_interface_payload() -> dict[str, object]:
|
|
1630
|
-
|
|
1840
|
+
def repo_interface_payload(profile_name: str = "execution-control") -> dict[str, object]:
|
|
1841
|
+
payload: dict[str, object] = {
|
|
1631
1842
|
"schema_version": "loom-repo-interface/v2",
|
|
1632
1843
|
"companion_entry": ".loom/companion/README.md",
|
|
1633
1844
|
"repo_specific_requirements": {"review": [], "merge_ready": [], "closeout": []},
|
|
@@ -1648,6 +1859,9 @@ def repo_interface_payload() -> dict[str, object]:
|
|
|
1648
1859
|
"status_locator": ".loom/companion/releases/status.json",
|
|
1649
1860
|
},
|
|
1650
1861
|
}
|
|
1862
|
+
if profile_name == "attach-only":
|
|
1863
|
+
payload["host_truth_locators"] = ATTACH_ONLY_HOST_TRUTH_LOCATORS
|
|
1864
|
+
return payload
|
|
1651
1865
|
|
|
1652
1866
|
|
|
1653
1867
|
def repo_interop_payload() -> dict[str, object]:
|
|
@@ -1924,6 +2138,10 @@ def scaffold_target(
|
|
|
1924
2138
|
profile_name = str(profile.get("name")) if isinstance(profile, dict) else "execution-control"
|
|
1925
2139
|
writes_light_loop = profile_name in {"light-governance", "execution-control", "strong-governance"}
|
|
1926
2140
|
writes_work_item_carriers = profile_has_work_item_carriers(profile_name)
|
|
2141
|
+
if profile_name == "attach-only":
|
|
2142
|
+
forbidden_errors = attach_only_forbidden_carrier_errors(target_root, result)
|
|
2143
|
+
if forbidden_errors:
|
|
2144
|
+
raise RuntimeError("; ".join(forbidden_errors))
|
|
1927
2145
|
|
|
1928
2146
|
writes: list[tuple[Path, str | dict[str, object], str]] = [
|
|
1929
2147
|
(target_root / ".loom/README.md", render_loom_readme(result), "text"),
|
|
@@ -1933,7 +2151,7 @@ def scaffold_target(
|
|
|
1933
2151
|
(target_root / ".loom/bootstrap/capability-map.md", render_capability_map(result), "text"),
|
|
1934
2152
|
(target_root / ".loom/companion/README.md", render_companion_readme(result), "text"),
|
|
1935
2153
|
(target_root / ".loom/companion/manifest.json", companion_manifest_payload(), "json"),
|
|
1936
|
-
(target_root / ".loom/companion/repo-interface.json", repo_interface_payload(), "json"),
|
|
2154
|
+
(target_root / ".loom/companion/repo-interface.json", repo_interface_payload(profile_name), "json"),
|
|
1937
2155
|
(target_root / ".loom/companion/interop.json", repo_interop_payload(), "json"),
|
|
1938
2156
|
(target_root / ".loom/companion/checkpoints.md", render_companion_checkpoints(), "text"),
|
|
1939
2157
|
(target_root / ".loom/companion/review.md", render_companion_review(), "text"),
|
|
@@ -2118,7 +2336,9 @@ def verify_target(target_root: Path, output_path: Path) -> list[str]:
|
|
|
2118
2336
|
"adoption_intent",
|
|
2119
2337
|
"detected_repository_mode",
|
|
2120
2338
|
"risk_summary",
|
|
2339
|
+
"required_carriers",
|
|
2121
2340
|
"planned_writes",
|
|
2341
|
+
"forbidden_authored_carriers",
|
|
2122
2342
|
"deferred_capabilities",
|
|
2123
2343
|
"upgrade_triggers",
|
|
2124
2344
|
"fact_chain",
|
|
@@ -2188,6 +2408,12 @@ def verify_target(target_root: Path, output_path: Path) -> list[str]:
|
|
|
2188
2408
|
for forbidden in (".loom/work-items/INIT-0001.md", ".loom/progress/INIT-0001.md", ".loom/status/current.md"):
|
|
2189
2409
|
if forbidden in declared_generated:
|
|
2190
2410
|
errors.append(f"deep-existing-repo bootstrap must not declare generated carrier `{forbidden}`")
|
|
2411
|
+
forbidden_profile = result.get("forbidden_authored_carriers")
|
|
2412
|
+
if not isinstance(forbidden_profile, list) or {
|
|
2413
|
+
str(item.get("path")) for item in forbidden_profile if isinstance(item, dict)
|
|
2414
|
+
} != {carrier["path"] for carrier in ATTACH_ONLY_FORBIDDEN_AUTHORED_CARRIERS}:
|
|
2415
|
+
errors.append("attach-only init-result must declare the full `forbidden_authored_carriers` profile list")
|
|
2416
|
+
errors.extend(attach_only_forbidden_carrier_errors(target_root, result))
|
|
2191
2417
|
if profile_name == "light-governance":
|
|
2192
2418
|
declared_generated = {
|
|
2193
2419
|
artifact.get("path")
|
|
@@ -57,7 +57,7 @@ description: 负责把仓库接入 Loom 的初始化场景入口。Use when Code
|
|
|
57
57
|
2. `judge`
|
|
58
58
|
- 判断这是 `新项目`、`小型既有仓库` 还是 `复杂既有仓库`
|
|
59
59
|
- 消费或输出 `adoption_intent`;当 intent 不明确且写入会创建重执行控制面时,先停在 decision prompt / dry-run,不静默写入
|
|
60
|
-
- 输出 `scaffold_profile`,并按该 profile 列出 required、planned 与
|
|
60
|
+
- 输出 `scaffold_profile`,并按该 profile 列出 required、planned、intentionally absent 与 forbidden authored carriers
|
|
61
61
|
- 输出本轮启用能力、暂不启用能力、升级触发条件、source locator、write target 与 validation command
|
|
62
62
|
3. `write`
|
|
63
63
|
- 只有用户要求实际落盘时才执行 `bootstrap --write`
|
|
@@ -66,7 +66,7 @@ description: Loom 的 root entry。负责初始化新项目或既有仓库,并
|
|
|
66
66
|
|
|
67
67
|
`--intent` 用来表达采用意图,而不是仓库静态分类。未显式给出 intent 时,dry-run 仍会输出推荐路径、风险摘要和计划写入载体;如果实际写入会创建重执行控制面,必须先显式选择 `execution-control` 或 `strong-governance`。
|
|
68
68
|
|
|
69
|
-
每个 intent 会收敛到一个 `scaffold_profile`。`observe-only` 与 `skill-install-only` 不写 adoption carriers;`attach-only` 只写 companion/read surfaces;`light-governance` 写 companion、review/spec 与 PR 最小闭环但不写 Loom-owned work/progress/status;`execution-control` 与 `strong-governance` 才写 Loom-owned execution carriers。
|
|
69
|
+
每个 intent 会收敛到一个 `scaffold_profile`。`observe-only` 与 `skill-install-only` 不写 adoption carriers;`attach-only` 只写 companion/read surfaces,并显式禁止 `.loom/work-items/**`、`.loom/progress/**`、`.loom/status/current.md`、`.loom/reviews/**`、`.loom/specs/**` 等 Loom-authored truth carriers;`light-governance` 写 companion、review/spec 与 PR 最小闭环但不写 Loom-owned work/progress/status;`execution-control` 与 `strong-governance` 才写 Loom-owned execution carriers。
|
|
70
70
|
|
|
71
71
|
## 1. 读取顺序
|
|
72
72
|
|
|
@@ -35,6 +35,8 @@
|
|
|
35
35
|
- `writes_artifacts`
|
|
36
36
|
- `writes_work_item_carriers`
|
|
37
37
|
- `description`
|
|
38
|
+
- `required_carriers`
|
|
39
|
+
- `forbidden_authored_carriers`
|
|
38
40
|
- 本轮启用的能力清单
|
|
39
41
|
- 每项能力分别映射到哪些 `governance`、`harness`、`templates`、`adoption` 规则
|
|
40
42
|
- 这次采用的是最小装配、轻量 retrofit 还是更完整装配
|
|
@@ -59,7 +61,9 @@
|
|
|
59
61
|
|
|
60
62
|
- `detected_repository_mode`:静态检测到的仓库模式和 scenario
|
|
61
63
|
- `risk_summary`:是否会写入重执行控制面、是否保护 repo-owned truth、是否需要显式 intent
|
|
64
|
+
- `required_carriers`:本 profile 必须落盘或保持可读的稳定载体
|
|
62
65
|
- `planned_writes`:dry-run / write 即将落盘的稳定载体集合
|
|
66
|
+
- `forbidden_authored_carriers`:本 profile 明确禁止生成、声明或保留的 Loom-authored truth carrier
|
|
63
67
|
- `intentionally_absent`:因 attach-only、light-governance、observe-only 或 skill-install-only 而明确不生成的载体
|
|
64
68
|
- 初始能力清单的承载位置
|
|
65
69
|
- 首批 `Work Item` 或等价事项清单的承载位置
|
|
@@ -119,6 +123,8 @@
|
|
|
119
123
|
- attach-only 必备工件是什么
|
|
120
124
|
- 哪些 repo-native carriers 继续保留
|
|
121
125
|
- 哪些 Loom-owned carriers 本轮不会生成
|
|
126
|
+
- `forbidden_authored_carriers` 必须至少包含 `.loom/work-items/**`、`.loom/progress/**`、`.loom/status/current.md`、`.loom/reviews/**`、`.loom/specs/**`
|
|
127
|
+
- verify 必须同时检查磁盘存在、`init-result` 声明、`planned_writes`、manifest artifacts 与 write touched,不得让 forbidden carrier 形成第二事实链
|
|
122
128
|
|
|
123
129
|
`init-result` 只允许承接 locator-only 信息,不并行复制实时停点、下一步、阻断项或最近验证摘要。
|
|
124
130
|
|
|
@@ -57,9 +57,13 @@
|
|
|
57
57
|
- `.loom/work-items/*`
|
|
58
58
|
- `.loom/progress/*`
|
|
59
59
|
- `.loom/status/current.md`
|
|
60
|
+
- `.loom/reviews/*`
|
|
61
|
+
- `.loom/specs/*`
|
|
60
62
|
- Loom-owned recovery/status carriers 的 bootstrap placeholder
|
|
61
63
|
- 对 branch / PR / worktree / merge / ruleset 的底层宿主重写
|
|
62
64
|
|
|
65
|
+
若上述 Loom-authored carriers 已存在、被 `init-result` / manifest 声明为 generated,或出现在 attach-only planned writes 中,verify 必须 fail closed。执行者只能迁移到宿主 truth locator、删除 competing carrier,或显式升级 intent 到 `execution-control`。
|
|
66
|
+
|
|
63
67
|
## 6. 升级信号
|
|
64
68
|
|
|
65
69
|
以下任一条件出现时,不应继续停留在 attach-only 默认路径:
|
|
@@ -162,6 +162,7 @@
|
|
|
162
162
|
- `policy_locators`
|
|
163
163
|
- `hook_locators`
|
|
164
164
|
- `release_targets`
|
|
165
|
+
- `host_truth_locators`
|
|
165
166
|
|
|
166
167
|
稳定约束:
|
|
167
168
|
|
|
@@ -171,8 +172,9 @@
|
|
|
171
172
|
- `policy_locators` 只在 `v2` 合法
|
|
172
173
|
- `hook_locators` 只在 `v2` 合法
|
|
173
174
|
- `release_targets` 只在 `v2` 合法
|
|
175
|
+
- `host_truth_locators` 只在 `v2` 合法,且只能声明宿主事实源 locator,例如 GitHub Issue、GitHub Project、PR review / guardian、PR metadata 与 issue state
|
|
174
176
|
- `v2` 不改变 `repo_specific_requirements` 与 `specialized_gates` 的既有纪律
|
|
175
|
-
- `v2` 不把 repo runtime state、review summary、validation status 或 retained host action result 写入 `repo-interface.json`
|
|
177
|
+
- `v2` 不把 repo runtime state、progress/current stop、review verdict、review summary、validation status、closeout result 或 retained host action result 写入 `repo-interface.json`
|
|
176
178
|
|
|
177
179
|
### 4.3 通用字段纪律
|
|
178
180
|
|
|
@@ -352,6 +352,7 @@ REPO_INTERFACE_V2_KEYS = REPO_INTERFACE_V1_KEYS | {
|
|
|
352
352
|
"policy_locators",
|
|
353
353
|
"hook_locators",
|
|
354
354
|
"release_targets",
|
|
355
|
+
"host_truth_locators",
|
|
355
356
|
}
|
|
356
357
|
DECLARED_LOCATOR_REQUIREMENTS = {"required", "optional", "advisory"}
|
|
357
358
|
DECLARED_LOCATOR_OWNERS = {"repo", "repo-companion", "host", "host-adapter", "platform", "external-tool"}
|