Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions .github/actions/build-shaft-mcp-live-jar/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,20 @@ runs:
- name: Build shaft-mcp with dependencies
shell: bash
run: |
mvn --batch-mode -pl shaft-mcp -am install -DskipTests -Dgpg.skip
mvn --batch-mode -pl shaft-mcp dependency:copy-dependencies -DincludeScope=runtime '-DoutputDirectory=${maven.multiModuleProjectDirectory}/shaft-mcp/target/dependency' -DskipTests -Dgpg.skip
for attempt in 1 2 3; do
if {
mvn --batch-mode -pl shaft-mcp -am install -DskipTests -Dgpg.skip \
&& mvn --batch-mode -pl shaft-mcp dependency:copy-dependencies -DincludeScope=runtime '-DoutputDirectory=${maven.multiModuleProjectDirectory}/shaft-mcp/target/dependency' -DskipTests -Dgpg.skip
status=$?
[ "$status" -eq 0 ]
}; then
exit 0
fi
if [ "$attempt" -lt 3 ]; then
sleep "$((attempt * 10))"
fi
done
exit "$status"
- name: Write shaft-mcp launcher argfile
shell: bash
run: |
Expand Down
1 change: 1 addition & 0 deletions .memory/events.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -641,3 +641,4 @@
{"actor":"agent","event":"memory.deleted","id":"decision.guidance-budgets-are-per-surface-since-3745-no-global-byte-pool","timestamp":"2026-08-01T16:13:37+03:00"}
{"actor":"agent","event":"memory.updated","id":"workflow.user-level-claude-harness-syncs-from-canonical-claude-user-harness-via-scripts-agents-sync-user-harness-py","timestamp":"2026-08-01T16:14:23+03:00"}
{"actor":"agent","event":"memory.updated","id":"workflow.verify-intellij-platform-sdk-api-shapes-against-the-real-installed-jars-jar-tf-javap-not-memory-before-writing-an-implementation-spec","timestamp":"2026-08-01T16:14:23+03:00"}
{"actor":"agent","event":"memory.created","id":"gotcha.live-launcher-retries-must-be-errexit-safe","timestamp":"2026-08-01T20:20:05+03:00"}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"body_path": "memory/gotchas/live-launcher-retries-must-be-errexit-safe.md",
"content_hash": "sha256:6211d78737bca116c53138fe0bd1e7bee75cebf25800911a23a848025be963ba",
"created_at": "2026-08-01T20:20:05+03:00",
"evidence": [],
"facets": {
"category": "gotcha"
},
"id": "gotcha.live-launcher-retries-must-be-errexit-safe",
"scope": {
"branch": null,
"kind": "project",
"project": "project.shaft-engine",
"task": null
},
"source": {
"kind": "agent",
"task": "Resolve #4373 Maven Central nightly failure"
},
"status": "active",
"tags": [],
"title": "Live launcher retries must be errexit-safe",
"type": "gotcha",
"updated_at": "2026-08-01T20:20:05+03:00"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The shared build-shaft-mcp-live-jar composite action runs under Bash -e. Keep its Maven install and dependency-copy pair inside an if condition; a bare `mvn && mvn; status=$?` exits on a failing second command before retry. Behavioral tests must cover retry after the second command fails and final failure propagation.
4 changes: 4 additions & 0 deletions scripts/agents/guard.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
# and .memory/memory/gotchas/
# unscoped-am-mvn-test-can-crash-the-jvm-across-the-whole-reactor.md --
# both repo-tracked so they travel with every clone/worktree)
# CI exception: e2eTests.yml's JUnit E2E reactor job deliberately builds
# dependencies with -am. It remains bounded by -pl shaft-engine, its
# -Dtest=Junit*,MoonTests default, and -DheadlessExecution=true, unlike
# the local test commands this guard protects.
# R2 Never auto-open/serve Allure reports
# R3 Never run GUI-opening commands on Windows (AGENTS.md Windows/Codex Safety)
# R8 Deny mutating `git stash` subcommands (pop/drop/apply/clear/push, and
Expand Down
113 changes: 113 additions & 0 deletions tests/scripts/test_build_shaft_mcp_live_jar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import os
import re
import subprocess

Check notice on line 3 in tests/scripts/test_build_shaft_mcp_live_jar.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/scripts/test_build_shaft_mcp_live_jar.py#L3

Consider possible security implications associated with the subprocess module.
import tempfile
import unittest
from pathlib import Path


ACTION_PATH = Path(__file__).resolve().parents[2] / ".github" / "actions" / "build-shaft-mcp-live-jar" / "action.yml"


class BuildShaftMcpLiveJarTest(unittest.TestCase):
@staticmethod
def _build_script() -> str:
action = ACTION_PATH.read_text(encoding="utf-8")
return action.split(" run: |\n", 1)[1].split(
" - name: Write shaft-mcp launcher argfile", 1
)[0]

def test_launcher_build_retries_the_complete_maven_sequence(self):
build_script = self._build_script()

self.assertIn("for attempt in 1 2 3; do", build_script)
self.assertIn('sleep "$((attempt * 10))"', build_script)
self.assertIn('exit "$status"', build_script)
self.assertRegex(
build_script,
re.compile(
r"mvn --batch-mode -pl shaft-mcp -am install .*?\n"
r"\s*&& mvn --batch-mode -pl shaft-mcp dependency:copy-dependencies .*?\n"
r"\s*status=\$\?",
re.DOTALL,
),
)

def test_launcher_build_retries_after_copy_dependencies_fails_under_errexit(self):
with tempfile.TemporaryDirectory() as temporary_directory:
temporary_path = Path(temporary_directory)
maven_log = temporary_path / "maven.log"
fake_maven = temporary_path / "mvn"
fake_maven.write_bytes(
b"#!/usr/bin/env bash\n"
b"log=\"$(dirname \"$0\")/maven.log\"\n"
b"printf '%s\\n' \"$*\" >> \"$log\"\n"
b"if [ \"$(wc -l < \"$log\")\" -eq 2 ]; then\n"
b" exit 17\n"
b"fi\n"
)
fake_sleep = temporary_path / "sleep"
fake_sleep.write_bytes(b"#!/usr/bin/env bash\n")
fake_maven.chmod(0o755)
fake_sleep.chmod(0o755)
environment = os.environ | {
"PATH": f"{temporary_path}{os.pathsep}{os.environ['PATH']}",
}

completed = subprocess.run(

Check warning on line 57 in tests/scripts/test_build_shaft_mcp_live_jar.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/scripts/test_build_shaft_mcp_live_jar.py#L57

Starting a process with a partial executable path

Check warning on line 57 in tests/scripts/test_build_shaft_mcp_live_jar.py

View check run for this annotation

Codacy Production / Codacy Static Code Analysis

tests/scripts/test_build_shaft_mcp_live_jar.py#L57

subprocess call - check for execution of untrusted input.
["bash", "-e"],
input=(
b'PATH="$(dirname "$(command -v mvn)"):$PATH"\n'
+ self._build_script().encode()
),
capture_output=True,
env=environment,
)

self.assertEqual(completed.returncode, 0, completed.stderr.decode())
maven_commands = maven_log.read_text(encoding="utf-8").splitlines()
self.assertEqual(len(maven_commands), 4)
self.assertIn("install", maven_commands[0])
self.assertIn("dependency:copy-dependencies", maven_commands[1])
self.assertIn("install", maven_commands[2])
self.assertIn("dependency:copy-dependencies", maven_commands[3])

def test_launcher_build_propagates_the_final_copy_dependencies_failure(self):
with tempfile.TemporaryDirectory() as temporary_directory:
temporary_path = Path(temporary_directory)
maven_log = temporary_path / "maven.log"
fake_maven = temporary_path / "mvn"
fake_maven.write_bytes(
b"#!/usr/bin/env bash\n"
b"log=\"$(dirname \"$0\")/maven.log\"\n"
b"printf '%s\\n' \"$*\" >> \"$log\"\n"
b"case \"$*\" in *dependency:copy-dependencies*) exit 17;; esac\n"
)
fake_sleep = temporary_path / "sleep"
fake_sleep.write_bytes(b"#!/usr/bin/env bash\n")
fake_maven.chmod(0o755)
fake_sleep.chmod(0o755)
environment = os.environ | {
"PATH": f"{temporary_path}{os.pathsep}{os.environ['PATH']}",
}

completed = subprocess.run(
["bash", "-e"],
input=(
b'PATH="$(dirname "$(command -v mvn)"):$PATH"\n'
+ self._build_script().encode()
),
capture_output=True,
env=environment,
)

self.assertEqual(completed.returncode, 17, completed.stderr.decode())
maven_commands = maven_log.read_text(encoding="utf-8").splitlines()
self.assertEqual(len(maven_commands), 6)
for install, copy_dependencies in zip(maven_commands[::2], maven_commands[1::2]):
self.assertIn("install", install)
self.assertIn("dependency:copy-dependencies", copy_dependencies)


if __name__ == "__main__":
unittest.main()
Loading