76 lines
2.9 KiB
Bash
Executable File
76 lines
2.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
DEVKIT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
WORK="$(mktemp -d)"
|
|
trap 'rm -rf "$WORK"' EXIT
|
|
TARGET="$WORK/existing-project"
|
|
mkdir -p "$TARGET/.github"
|
|
printf '# Existing agent rules\n' > "$TARGET/AGENTS.md"
|
|
printf '# Existing Claude rules\n' > "$TARGET/CLAUDE.md"
|
|
printf '# Existing Copilot rules\n' > "$TARGET/.github/copilot-instructions.md"
|
|
|
|
install_project() {
|
|
bash "$DEVKIT_ROOT/install.sh" \
|
|
--target "$TARGET" \
|
|
--project sample-project \
|
|
--domain "Sample Project" >/dev/null
|
|
}
|
|
|
|
install_project
|
|
|
|
python3 - "$TARGET" <<'PY'
|
|
import json, pathlib, sys
|
|
root = pathlib.Path(sys.argv[1])
|
|
policy = json.loads((root / ".casan/prompt-policy.json").read_text(encoding="utf-8"))
|
|
assert policy["mode"] == "enforced"
|
|
assert policy["project_id"] == "sample-project"
|
|
assert policy["domain_root"] == "apps/sample-project/domain"
|
|
for relative in ("AGENTS.md", "CLAUDE.md", ".github/copilot-instructions.md"):
|
|
text = (root / relative).read_text(encoding="utf-8")
|
|
assert "Existing" in text
|
|
assert text.count("CASAN_PROMPT_ENFORCEMENT_START") == 1
|
|
workflow = (root / ".gitea/workflows/casan-prompt-enforcement.yml").read_text(encoding="utf-8")
|
|
assert "CASAN prompt enforcement contract" in workflow
|
|
assert 'CASAN_PROJECT_ID: "sample-project"' in workflow
|
|
assert "__PROJECT_ID__" not in workflow
|
|
assert (root / "bin/casan-chat").exists()
|
|
assert (root / "bin/casan-chat.ps1").exists()
|
|
PY
|
|
|
|
(cd "$TARGET" && bin/casan prompt verify) | grep -q 'CASAN_PROMPT_ENFORCEMENT_VALID project=sample-project'
|
|
|
|
# A second adoption run upgrades managed files without duplicating instruction blocks.
|
|
install_project
|
|
python3 - "$TARGET" <<'PY'
|
|
import pathlib, sys
|
|
root = pathlib.Path(sys.argv[1])
|
|
for relative in ("AGENTS.md", "CLAUDE.md", ".github/copilot-instructions.md"):
|
|
assert (root / relative).read_text(encoding="utf-8").count("CASAN_PROMPT_ENFORCEMENT_START") == 1
|
|
PY
|
|
|
|
# Managed-file tampering must fail closed before a prompt reaches chat-turn.
|
|
python3 - "$TARGET/CLAUDE.md" <<'PY'
|
|
import pathlib, sys
|
|
path = pathlib.Path(sys.argv[1])
|
|
path.write_text(path.read_text(encoding="utf-8").replace("CASAN_PROMPT_ENFORCEMENT_START", "REMOVED_MARKER"), encoding="utf-8")
|
|
PY
|
|
set +e
|
|
(cd "$TARGET" && bin/casan prompt verify >"$WORK/tamper.out" 2>&1)
|
|
TAMPER_RC=$?
|
|
set -e
|
|
if [[ "$TAMPER_RC" -ne 2 ]] || ! grep -q 'managed_marker_missing:CLAUDE.md' "$WORK/tamper.out"; then
|
|
echo "adoption-install-tests: tampered contract was accepted" >&2
|
|
cat "$WORK/tamper.out" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# New-project scaffolding must continue into the same enforcement installation.
|
|
FRESH="$WORK/fresh-project"
|
|
bash "$DEVKIT_ROOT/install.sh" --target "$FRESH" --project fresh-app \
|
|
--domain "Fresh App" --template nestjs-react >/dev/null
|
|
(cd "$FRESH" && bin/casan prompt verify) | grep -q 'CASAN_PROMPT_ENFORCEMENT_VALID project=fresh-app'
|
|
test -f "$FRESH/apps/fresh-app/frontend/package.json"
|
|
|
|
echo "ADOPTION_INSTALL_TESTS_PASS"
|