Files
2026-07-13 12:40:00 +08:00

85 lines
3.3 KiB
Python

"""Location parity for dual-shipped scripts (root scripts/ vs skill scripts/).
The hook dispatchers resolve scripts from three locations: the skill install dir
(``${CLAUDE_SKILL_DIR}/scripts``), the bare-skill known path
(``~/.claude/skills/planning-with-files/scripts``), and the plugin-marketplace
fallback (``~/.claude/plugins/marketplaces/planning-with-files/scripts``). The
marketplace path is the REPO ROOT ``scripts/`` directory, so every script the
dispatchers call — directly or as a sibling (inject-plan.sh shells
``${SCRIPT_DIR}/ledger-summary.sh``) — must exist in BOTH locations with
identical content.
This was a live bug before this test existed: the v3 review wave restored
inject-plan.sh and gate-stop.sh to root scripts/ but not the ledger trio, so the
marketplace route in autonomous/gated mode silently fell back to injecting the
raw progress.md tail — the exact surface the structured ledger summary is
documented to close (SKILL.md "Structured ledger injection").
"""
from __future__ import annotations
import unittest
from pathlib import Path
REPO_ROOT = Path(__file__).resolve().parents[1]
ROOT_SCRIPTS = REPO_ROOT / "scripts"
SKILL_SCRIPTS = REPO_ROOT / "skills" / "planning-with-files" / "scripts"
# Every script the hook dispatchers reach from a scripts/ directory, in both
# shell and PowerShell forms. Sibling dependencies count: inject-plan.sh calls
# ledger-summary.sh, gate-stop.sh calls check-complete.sh, init-session calls
# attest-plan and resolve-plan-dir, ledger-summary/phase-status back the
# autonomous-mode injection and the gate's phase reporting.
DUAL_SHIPPED = [
"attest-plan.ps1",
"attest-plan.sh",
"check-complete.ps1",
"check-complete.sh",
"gate-stop.sh",
"init-session.ps1",
"init-session.sh",
"inject-plan.sh",
"ledger-append.ps1",
"ledger-append.sh",
"ledger-summary.ps1",
"ledger-summary.sh",
"phase-status.ps1",
"phase-status.sh",
"resolve-plan-dir.ps1",
"resolve-plan-dir.sh",
"set-active-plan.ps1",
"set-active-plan.sh",
]
class ScriptLocationParityTests(unittest.TestCase):
def test_dual_shipped_scripts_exist_in_both_locations(self) -> None:
for name in DUAL_SHIPPED:
with self.subTest(script=name):
self.assertTrue(
(ROOT_SCRIPTS / name).is_file(),
f"scripts/{name} missing — plugin-marketplace fallback route breaks",
)
self.assertTrue(
(SKILL_SCRIPTS / name).is_file(),
f"skills/planning-with-files/scripts/{name} missing — skill install route breaks",
)
def test_dual_shipped_scripts_are_byte_identical(self) -> None:
for name in DUAL_SHIPPED:
root_file = ROOT_SCRIPTS / name
skill_file = SKILL_SCRIPTS / name
if not (root_file.is_file() and skill_file.is_file()):
continue # existence failures are reported by the test above
with self.subTest(script=name):
self.assertEqual(
root_file.read_bytes(),
skill_file.read_bytes(),
f"{name} drifted between scripts/ and skill scripts/ — "
"the two install routes would behave differently",
)
if __name__ == "__main__":
unittest.main()