mirror of
https://github.com/OthmanAdi/planning-with-files.git
synced 2026-09-14 16:09:40 +08:00
0d286ac530
Every failure in the resolution and injection class is silent by design (hooks always exit 0), so a broken install looks identical to "no plan yet". plan-doctor.sh answers in one pass: does resolution work here and which plan wins, does injection emit plan context, what path shape does the canonicalizer produce, is the plan attested, which install surfaces exist, and what one hook fire costs in wall-clock. Dual-shipped in both scripts/ locations and added to the parity test. From the July 2026 benchmark improvement backlog (P0 trigger and install-health items).
86 lines
3.3 KiB
Python
86 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",
|
|
"plan-doctor.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()
|