"""The stack's shipped instructions read the same in bash, Git Bash and PowerShell 7. `instructions/CONTRACT.md` ยง Writing an instruction states the rule; this is the check behind it. An instruction is run by whichever harness the operator uses - Claude Code under Git Bash on Windows, Copilot under PowerShell 7 - so a command block that only one shell reads sends the other agent off to translate it, which is where the Weg-D install went wrong (Gitea #140, #153). Scope: every instruction `dist export` ships, minus `instructions/migrations/`, which belong to the release they shipped with. A test rather than an `instructions verify` rule because the rule binds what the stack ships; an instance's own instructions are its own decision. """ from __future__ import annotations import re from pathlib import Path import pytest REPO = Path(__file__).resolve().parents[3] INSTRUCTIONS = REPO / "instructions" # The info strings of a block an agent runs; a json/yaml/markdown block is content, not a command. COMMAND_FENCES = {"", "bash", "sh", "shell", "console", "powershell", "pwsh"} FORBIDDEN = ( ("heredoc", re.compile(r"<<")), ("export", re.compile(r"(^|[;&|]\s*)export\s")), ("command substitution", re.compile(r"\$\(")), ("shell variable", re.compile(r"\$\{|\$[A-Z_][A-Z0-9_]*\b")), ("inline environment", re.compile(r"^[A-Z_][A-Z0-9_]*=\S*\s+\S")), ("&&", re.compile(r"&&")), ("for loop", re.compile(r"^\s*for\s.*;\s*do\b")), ("cp", re.compile(r"(^|[;&|]\s*)cp\s")), ("cat >", re.compile(r"\bcat\s+>")), ("sha256sum", re.compile(r"\bsha256sum\b")), ("curl", re.compile(r"\bcurl\b")), ("tar", re.compile(r"(^|[;&|]\s*)tar\s")), # Which name starts a real Python differs per shell and platform: on Windows `python3` - in # PowerShell also `python` - can be the Microsoft Store's alias. A command goes through a # launcher that finds the interpreter itself (`tools/wikitool`, `tools/bugreport`). ("bare interpreter", re.compile(r"(^|[;&|]\s*)(python3?|py)(\.exe)?\s")), ) # The two sanctioned exceptions, each one line per shell (instructions/CONTRACT.md): the session # id (D26) and the preflight download before an instance exists (E2). Keyed by exact line, so a # second use of the same construct elsewhere still fails. ALLOWED = { ("session-setup.md", 'export WIKITOOL_SESSION_ID="wiki-20261001-1430"'), ("session-setup.md", "$env:WIKITOOL_SESSION_ID = 'wiki-20261001-1430'"), ("setup-instance.md", "curl -fLO "), } def shipped_instructions() -> list[Path]: return sorted( path for path in INSTRUCTIONS.rglob("*.md") if not {"dev", "migrations"} & set(path.relative_to(INSTRUCTIONS).parts[:-1]) ) def command_lines(path: Path): """`(line number, line)` for every line inside a command fence.""" fence = None for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): stripped = line.strip() if stripped.startswith("```"): fence = None if fence is not None else stripped[3:].strip() continue if fence is not None and fence in COMMAND_FENCES and stripped: yield number, stripped def test_the_scope_is_not_empty(): names = {path.name for path in shipped_instructions()} assert {"setup-instance.md", "session-setup.md", "upgrade-instance.md"} <= names assert not any("migrations" in path.parts for path in shipped_instructions()) @pytest.mark.parametrize("path", shipped_instructions(), ids=lambda p: str(p.relative_to(INSTRUCTIONS))) def test_a_shipped_instruction_uses_no_shell_specific_syntax(path): found = [ f"{path.relative_to(REPO)}:{number}: {name}: {line}" for number, line in command_lines(path) if (path.name, line) not in ALLOWED for name, pattern in FORBIDDEN if pattern.search(line) ] assert not found, "\n".join(found) def test_every_exception_is_still_in_use(): """An allowance nobody uses any more is a gap waiting for the next construct.""" used = { (path.name, line) for path in shipped_instructions() for _, line in command_lines(path) } assert ALLOWED <= used, ALLOWED - used SHIPPED_ROOT_DOCS = ("AGENTS.md", "README.md", "INSTALL.md") def shipped_docs() -> list[Path]: """The root documents an operator reads, plus every page `dist export` ships under `docs/` - the how-to guides among them name the preflight call an operator types.""" docs = REPO / "docs" pages = sorted( path for path in docs.rglob("*.md") if path.relative_to(docs).parts[0] != "dev" ) return [REPO / name for name in SHIPPED_ROOT_DOCS] + pages # A line that starts the PowerShell preflight - bare (`.\tools\preflight.ps1`, `& preflight.ps1`) # or through pwsh - as opposed to one that merely names the file (a comment, a download). PREFLIGHT_CALL = re.compile(r"^(?:&\s*)?(?:\.[\\/])?(?:tools[\\/])?preflight\.ps1\b|^pwsh\b.*preflight\.ps1") BYPASS = "pwsh -NoProfile -ExecutionPolicy Bypass -File " @pytest.mark.parametrize( "path", shipped_docs() + shipped_instructions(), ids=lambda p: str(p.relative_to(REPO)), ) def test_every_powershell_preflight_call_carries_the_bypass(path): """Copilot started `.\\tools\\preflight.ps1` bare in the #151 hand check; in a checkout that carries a Mark of the Web that call fails with PowerShell's own refusal and no guidance.""" if not path.is_file(): pytest.skip(f"{path.name} is not in this tree") bare = [ f"{number}: {line}" for number, line in command_lines(path) if PREFLIGHT_CALL.match(line) and not line.startswith(BYPASS) ] assert not bare, "\n".join(bare)